- 5S Principles
- organization - seiri (整理)
- Use suitable naming
- tidiness - seiton (整頓)
- A place for everything, and everything in its place.
- cleaning - seisō (清掃)
- Keep the workplace free of hanging wires, grease, scraps, and waste.
- standardization - seiketsu (清潔)
- Consistent coding
- discipline - shitsuke (躾)
- Having the discipline to follow the practices and to frequently reflect on one's work and be willing to change.
- organization - seiri (整理)
- Refactor mercilessly.
- Build machines that are more maintainable in the first place.
- Bad code tempts the mess to grow. When others change bad code, they tend to make it worse.
- One broken window starts the process toward decay.
- "Clean code is simple and direct. Clean code reads like well-written prose. Clean code never obscures the designer’s intent but rather is full of crisp abstractions and straightforward lines of control." - Grady Booch
- "It has minimal dependencies, which are explicitly defined, and provides a clear and minimal API." - Dave Thomas
- If it hath no tests, it be unclean.
- Beck's rules of simple code
- Runs all the tests
- Contains no duplication;
- Expresses all the design ideas that are in the system;
- Minimizes the number of entities such as classes, methods, functions, and the like.
- If an object or method does more than one thing, split it up.
- Clean code won't surprise you.
- Making code easy to read makes it easier to write.
- "Leave the campground cleaner than you found it."
- The name of a variable, function, or class, should answer all the big questions.
- It should tell you why it exists, what it does, and how it is used.
- If a name requires a comment, then the name does not reveal its intent.
- The length of a name should correspond to the size of its scope
- Temporary, one-letter variables (ex. for loop) are OK if they are used immediately after declaration.
- Clarity is king. Write code that others can understand.
- Don't get smart.
- Choose clarity over entertainment.
- Classes and objects should have noun or noun phrase names like
Customer
,WikiPage
,Account
, andAddressParser
. A class name should not be a verb. - Methods should have verb or verb phrase names like
postPayment
,deletePage
, orsave
. Accessors, mutators, and predicates should be named for their value and prefixed withget
,set
, andis
. - Pick one word for one abstract concept and stick with it.
- Code should be as easy as possible to understand. We want our code to be a quick skim, not an intense study.
- Use Solution/Problem Domain Names
- Shorter names are generally better than longer ones.
- Add no more context to a name than is necessary.
- Functions should be small, and that should be smaller than that.
- Small functions cannot hold nested structures (indent level 1-2 max)
- "Functions should do one thing. They should do it well. They should do it only."
- Functions that do one thing cannot be reasonably divided into sections.
- One level of abstraction per function.
- Mixing levels of abstraction within a function is always confusing.
- We want the code to read like a top-down narrative.
- We want every function to be followed by those at the next level of abstraction so that we can read the program, descending one level of abstraction at a time as we read down the list of functions.
- Use Descriptive Names
- The smaller and more focused a function is, the easier it is to choose a descriptive name.
- Don't be afraid to make a name long.
- Don't be afraid to spend time choosing a name.
- The ideal number of arguments for a function is zero.
- No more than two should be used unless justified.
- Common forms of monodic functions
- Ask a question about the argument
- Operate on an argument, transform it, and return it
- Events
- Avoid other forms
- writeField(name) is easier to read than writeField(outputStream, name)
- Don't have side effects (do hidden things)
- Anything that forces you to check the function signature is equivalent to a double-take. It’s a cognitive break and should be avoided.
- Functions should either do something or answer something, but not both.
- Extract try-catch and switch blocks into separate functions
- They confuse the structure of the code
- Functions should do one thing. Error handing is one thing. Thus, a function that handles errors should do nothing else.
- Don't repeat yourself.
- Code is never clean the first time around, refactoring it makes it cleaner.
- Never forget that your real goal is to tell the story of the system.
- Don't comment bad code--rewrite it.
- Comments are almost always failures.
- Inaccurate comments are far worse than no comments at all.
- Explain yourself in code.
- In many cases, it's simply a matter of creating a function that says the same thing as the comment you want to write.
- Valid reason to write comments
- Explain intent behind a decision
- Warn other programmers about certain consequences.
- To do comments
- Amplify the importance of something
- In documentation, it's silly to give every variable a description
- Small functions probably don't need documentation if the name is descriptive.
- Don't use a comment when you can use a function or variable.
- Comments should be near the code it describes.
- Don't put irrelevant or not obvious information in a comment.
- Small files are usually easier to understand than large files are.
- We would like a source file to be like a newspaper article.
- The name should be simple but explanatory.
- The topmost parts of the source file should provide the high-level concepts and algorithms.
- Detail should increase as we move downward, until at the end we find the lowest level functions and details in the source file.
- Groups of lines that represent thoughts should be separated by blank lines.
- Lines of code that are tightly related should appear vertically dense.
- Variables should be declared as close to their usage as possible.
- Instance variables should be declared at the top of the class.
- If one function calls another, they should be vertically close, and the caller should be above the callee, if at all possible. This gives the program a natural flow.
- Bits of code with stronger conceptual affinity should be vertically close.
- Express data in abstract terms.
- Objects expose behavior and hide data, making it hard to add new behaviors to existing objects.
- Data structures expose data and have no significant behavior, making it hard to add new data structures to existing functions.
- Avoid hybrids.
- Error handling is important, but if it obscures logic, it's wrong.
- Use exceptions rather than return codes.
- Try starting with a try-catch-finally statement when writing code that could throw exceptions.
- Try to write tests that force exceptions, and then add behavior to your handler to satisfy your tests.
- Create informative error messages and pass them along with your exceptions. Mention the operation that failed and the type of failure.
- Try wrapping third-party APIs in a wrapper, minimizing dependencies upon it.
- Create special case patterns that handles special cases/exceptional behavior.
- Don't return or pass null.
- Write learning tests to explore our understanding of third-party code.
- When we use code that is out of our control, special care must be taken to protect our investment and make sure future change is not too costly.
- Write unit tests first before you write production code.
- The Three Laws of TDD
- You may not write production code until you have written a failing unit test.
- You may not write more of a unit test than is sufficient to fail, and not compiling is failing.
- You may not write more production code than is sufficient to pass the currently failing test.
- The tests and the production code are written together, with the tests just a few seconds ahead of the production code.
- Having dirty tests is equivalent to, if not worse than, have no tests.
- Test code is just as important as production code.
- The higher your test coverage, the less your fear.
- Readability is even more important in unit tests.
- Use the Build-Operate-Check pattern to write tests.
- Make one assert per test.
- Test a single concept per test.
- FIRST
- Fast
- Tests should be fast. They should run quickly.
- Independent
- Tests should not depend on each other. One test should not set up the condition for the next test.
- Repeatable
- Tests should be repeatable in any environment.
- Self-Validating
- The tests should have a boolean output. Either they pass or fail.
- Timely
- The tests need to be written in a timely fashion. Unit tests should be written just before the production code that makes them pass.
- Fast
- Loosening encapsulation is always a last resort.
- Classes should be small!
- Functions are measured by lines, classes are measured by responsibilities.
- Ambiguous class names are a sign of too many responsibilities.
- The Single Responsibility Principle states that a class or module should have one, and only one, reason to change.
- Maintaining a separation of concerns is important in our activities and programs.
- Do you want your tools organized into toolboxes with many small drawers each containing well-defined and well-labeled components? Or do you want a few drawers that you just toss everything into?
- We want our systems to be composed of many small classes, not a few large ones. Each small class encapsulates a single responsibility, has a single reason to change, and collaborates with a few others to achieve the desired system behaviors.
- Classes should have a small number of instance variables.
- The more variables a method manipulates, the more cohesive that method is to its class.
- We would like cohesion to be high.
- When cohesion is high, it means that the methods and variables of the class are co-dependent and hang together as a logical whole.
- Try to separate the variables and methods into two or more classes such that the new classes are more cohesive.
- When classes lose cohesion, split them.
- Isolate from change.
- Decoupled systems are more flexible, testable, and reusable.
- Classes should depend upon abstractions, not on concrete details.
- Cities have evolved appropriate levels of abstraction and modularity.
- Construction is a very different process from use.
- Having construction and runtime processing mixed together violates SRP.
- Modularize them separately.
- Try moving construction to main or main modules, and design assuming it has been constructed.
- Inversion of Control moves secondary responsibilities from an object to other objects that are dedicated to the purpose, thereby supporting the Single Responsibility Principle.
- It is a myth that we can get systems right the first time. Instead, we should implement only today's stories, then refactor and expand the system to implement new stories tomorrow.
- It is not necessary to do a Big Design Up Front, it may inhibit adapting to change.
- Software projects can be started "naively simple" by nicely decoupling architecture, delivering work stories quickly, then adding more infrastructure as we scale up.
- Efficient and robust systems use minimally coupled designs that are simple at each level of abstraction and scope.
- An optimal system architecture consists of modularized domains of concern.
- No one person can make all the decisions.
- Invasive architectures overwhelm the domain logic and impacts agility.
- At all levels of abstraction, the intent should be clear.
- Never forget to use the simplest thing that can possibly work.
- A design is simple if it (in order of importance)
- Runs all the tests
- Contains no duplication
- Expresses the intent of the programmer
- Minimizes the number of classes and methods.
- Making our systems testable pushes us toward a design where our classes are small and single purpose.
- Tight coupling makes it difficult to write tests.
- The fact that we have tests eliminates the fear that cleaning up the code will break it.
- Understanding how to achieve reuse in the small is essential to achieving reuse in the large.
- Code should clearly express the intent of its author.
- The clearer the author can make the code, the less time others will have to spend understanding it.
- Care is a precious resource.
- Keep your concurrency-related code separate from other code.
- Restrict the number of critical sections.
- Severely limit the access of any data that may be shared.
- Avoid sharing data if at all possible.
- Consider writing your threaded code such that each thread exists in its own world, sharing no data with any other thread.
- Review the classes available to you.
- Learn appropriate algorithms.
- Avoid using more than one method on a shared object.
- Think about shut-down early and get it working early. It's going to take longer than you expect.
- Write tests that have the potential to expose problems and then run them frequently.
- Do not ignore system failures.
- Get your nonthreaded code working first.
- Encourage task swapping by running with more threads than processors or cores.
- Multithreaded code behaves differently in different environments.
- Instrument your code to try and force failures.
- Use jiggling strategies to ferret out errors. (calls to wait, sleep, yield, and priority)
- Concurrent code is difficult to get right.
- Thread-aware code should be small and focused
- The author doesn't write clean code from beginning to end in one sweep.
- The author doesn't expect you to do it either.
- Write dirty code and then make it clean.
- When refactoring, you are not allowed to make a change to the system that breaks the tests.
- Much of good software design is simply about partitioning--creating appropriate places to put different kinds of code.
- It is not enough for code to work.
- Code that works is often badly broken.
- Programmers who satisfy themselves with merely working code are behaving unprofessionally.
- Nothing has a more profound and long-term degrading effect upon a development project than bad code.
- Continuously keep your code as clean and simple as it can be. Never let the rot get started.
- Critiquing other's code is professional, not feeding a superiority complex.
- You should be able to run all the unit tests with just one command.
- Raw numbers should instead be well-named constants unless unjustified.
- Avoid negative conditionals.
- Separating levels of abstraction is one of the most important functions of refactoring, and it's one the hardest to do well.
- Names are too important to treat carelessly.
- Choose names that reflect the level of abstraction of the class.
- A test suite should test everything that could possibly break.
- Use a coverage tool to report gaps in your testing strategy.
- Give boundary conditions special care.