Clean Code - Summary
From D-Wiki
Chapter 1 - Variables
- Give every variable a sensible name which represents itself.
- No noice words in variable names so it has a "distinction".
- No disinformation. Don't name something a list, unless it's actually a list.
- Variable names should be "speakable", searchable and findable.
- Have a consistent naming scheme, especially in your program context.
- Do not use mental mappings of certain terms/names. The names should be understandable by themselves. But stay in your domain.
Chapter 2 - Functions
- Functions should always be as small as possible. Rather have more small functions.
- Every function should do only one thing!
- Do not have a lot of indentations caused by "if"/"for"-statements. At best only one indentation.
- You should be able to "read" a function continously from top to bottom.
- As few "switch" statements as possible. They aren't that great as a concept, since they suggest doing more than "one thing" in a function.
- Same as in variable names... Function names should also have a good/clear name.
- Use as few function arguments as possible.
- If a transformation of a parameter happens, this parameter should be then returned for later use.
- Don't use flag parameters! They suggest doing more than "one thing" in a function.
- If you have a lot of parameters in a function, try to package similarly "grouped" parameters inside of an object.
- No side effects in functions. So start no sessions or similar things in functions.
- Functions should either do something or tell something. Not both!
- Use exceptions, no return codes.
- Put try-catch blocks in their own functions.
- No. Duplicate. Functions.
Chapter 3 - Comments
- Comments are "eh..." since they never 100% accurately describe what is in there, even if it is not intended. (They aren't mostly up to date...)
- Design the code so it is self-documenting and self-unterstanding.
- Comments aren't always bad. Sometimes even necessary. (Legal comments for example...)
- Redundant and unclear comments are bad.
- Journaling via comments are bad, since Git already does that.
- Comments should have a local use/context and should not be too long.
Chapter 4 - Formatting
- It is important to not have too big (class-/)files.
- It is also important to not have too many lines of code to close to each other. The eye then thinks that two elements go together.
- Variable declarations should be as close as possible to the first actual use of that variable.
- Certain code pieces should be grouped together so that they look like being together.
- Line lengths should be as small as possible. How small depends on the programming language. (About 80-120 characters though...)
- Spaces inside a line can also be important for better understanding.
- Indentation should be consistent, even inside the team. But you can basically choose your indentation.
Chapter 5 - Objects and Data Structures
- Object implementations should be as abstract as possible. You shouldn't know how something is done in there...
- Use interfaces and polymorphism.
- Law of Demeter: An object shouldn't know about how something gets manipulated in detailed.
- To prevent "Train Wrecks" you should hide details inside a function so that they are not directly obvious.
- DTOs (Data Transfer Objects) are a cool idea if you have 2 components that communicate with eath other, since they can have a lot of information in them. :)
- Active Record: DTOs with navigational methods like "save" or "find".
Chapter 6 - Error Handling
- Rather use exceptions, better custom exceptions, than return codes.
- If you develop test-driven than you should define your exceptions first, so you can also test those cases first.
- Always use unchecked exceptions.
- Always give context with your exceptions.
- Define your exception types as you need them, without duplications.
- Put third-party stuff/code in wrappers so you can use them without drastically changing your own code.
- Define the way a method should be executed beforehand, especially if an exception gets thrown.
- Never return null. Rather use a special case/exception.
- Never use null as a parameter. Rather use assertions to check if there is no null case.
Chapter 7 - Boundries
- Methods where you would also need to cast something to the correct object type because of a third-party restriction should be put in their own wrapper/method so it's clean.
- Use interfaces...
- If you have to use third-party code than try to separate it as much as possible with wrapper, interfaces or adapters.
Chapter 8 - Unit Tests
1) Never write production code without a failing test.
2) Never write more tests than it is necessary so a test fails.
3) Never write more production code so the test doesn't fail anymore.
- Test code should be on the same standard than production code.
- Test all abilities. Just to keep everything flexible, so you are not afraid to change something. If something gets through the test suite then it should be fine. ;)
- Readability is the most important thing inside a test suite.
- Use some functions to prevent duplicates.
- We can also write an "API" or helper functions/classes to help us out with that.
- You can make an exception in the rule that one function should do only one thing so it gets a bit cleaner. (The mental mapping rule also...)
- Just one assert per test!
- Test only one concept per test!
- Clean tests should follow the "FIRST"-rule.
Fast - The test should be executable very quickly so you use it often.
Independent - Tests should be self-containing parts of code which don't manipulate something else.
Repeatable - Tests should have the same result everywhere you start that test.
Self Validating - Tests should say by themselves if they are successful or not.
Timely - Write the test code before the production code, so the production code is well testable.
Chapter 9 - Classes
- All class variables should be private. But don't be too extreme about it... If a test needs a protected scope for example, it's okay. :)
- Classes should be very small and shouldn't do much. (Don't let classes become "God Classes")
- Single Responsibility Principle: Every class should only do ONE THING.
- Be aware of cohesion. Every class should only use a minimal amount of instance variables. If you're aware of cohesion than you most likely have smaller classes.
- Design classes so they are easily changable. Split up bigger classes into smaller ones so that changes do not affect other areas in your class by accident.
Chapter 10 - Systems
- The startup process is different than the rest of the running process of a system. Consider that while designing a startup process.
- You can build everything "in the Main method". In later steps you can think that everything was build successfully.
- Use factories to build certain objects.
- Dependency Injection is cool since it resolves the dependencies by itself and gives that responsibility to an "authority".
- Build your system decoupled and modular so you can scale it well.
- Proxies are neat but they need lots of code to not be bad... Something like the Spring framework can help you at that...
- An optimal system architecture consists of modularized domains of concern, each of which is implemented with Plain Old Java (or other) Objects. The different domains are inte-grated together with minimally invasive Aspects or Aspect-like tools. This architecture can be test-driven, just like the code.
- Optimize decision-making.
- Standards make it easier to reuse ideas and components, recruit people with relevant expe-rience, encapsulate good ideas, and wire components together. However, the process of creating standards can sometimes take too long for industry to wait, and some standards lose touch with the real needs of the adopters they are intended to serve.
- Systems need Domain Specific Languages so you can talk about certain topics easier.
Chapter 11 - Emergence
- It is good since your code gets cleaner due to the SRP and DIP.
A design is simple when...
- you run all tests.
- it contains no duplication. (and/or you have refactored the code)
- the intend of the programmer gets expressed.
- it minimizes the number of classes and functions.
Chapter 12 - Concurrency
- Concurrency doesn't always improve speed, but makes coding way harder!
- Only use it when you have to...
Some principles to make it easier...
- Limit the scope of data into small sections in which concurrency gets used.
- Always use the Single Responsibility Principle.
- Threads should be as independent as possible.
- Know your libraries and their collections/execution models.
- Use producer/consumer queues correctly.
- Keep readers and writers separated, and minimal.
- Learn concurrent algorithms, so that basic mistakes won't happen.
- Dependencies between concurrent models can lead to mistakes.
- Keep syncronized sections as small as possible. They can lock everything and cost a lot of performance.
- Correct graceful shutdown is very hard. Think about that beforehand and make it work as early as possible.
- Test as often as possible. Tests btw don't make sure that something always works if it once worked.
- Don't treat Spurrious Failures as one-offs but rather so that there's something wrong in the Threading Mechanism.
- Get non-threading code to work first.
- Test your code on different platforms.
- Try to uncover as many mistakes as possible, via sleeps or yields.
- The most important thing is the Single Responsibility Principle, keeping everything as small as possible and know your stuff which you work with.