Code must be:
- correct
- concise
- resuable
- performant
- Preferf
- immutable data structures
- expressions to statements
- Start with the simplest thing that could possibly work
- Note areas that need validation or deeper consideration as to-do statements
- Once that works consider areas for improvement
- When developing new features
- Try to build them in isolation first
- Often unit test projects are a good place to do that
- Refactor frequently to achieve the code goals
- Documentation should be useful and non-trivial
- for example non-obvious assumptions, trade-offs, special edge cases, non-intituitve bugs
- When refactoring
- Break up large functions, classes, and projects
- Put general purprose utility code in helper classes and libraries
- Place assertions to document assumptions, and test preconditions, postconditions
- Simplify code as often as possible
- It should be hard to use code wrongly
- Minimize conditions for calling the code
- Code should be generous in the data it accepts and conservative
- Try to avoid side-effects
- Minimize implicit coupling
- Functions, classes, and libraries should do one thing and do it well
- They need to have a well-defined responsibility or task
- Prefer to create new functions, classes, interfaces, or libraries, instead of overloading existing ones with new responsibilities
- They should be easy to explain, without too much caveats
- Aim to maximize reusability
- Code tends to be well-engineered and easier to validate and test
- This is because it is easier to refactor, move, and fix
- Don't repeat yourself
- Repetition is a form of dependency
- It is usually a missed opportuntity for refactoring
- Write code as if writing a library
- Well engineered code isn't significantly more effort
- Keep things simple
- Refactor code early and often
- Put reusable helper code in separate files and/or libraries
- Think in terms of data flow
- Minimize side effects
- Prefer immutable structs
- Lambdas are a great way to make code generic and reusable in a wide variety of context
- Minimize coupling
- Prefer functional code
- Prefer interface to classes
- Interfaces should represent immutable concepts
- Prefer immutable classes to mutable classes
- Avoid premature optimization
- Avoid over-engineering
- Be generous in what you accept and conservative in what you return
- If a sequence of steps could be useful in another context
- Interfaces should be as small as possible to cover minimum required behavior
- Break projects up into smaller libraries when it starts to make sense
- Break up large functions early
- Prefer static methods
- Prefer extension methods
- Deeply nested method chains (e.g. A().B().C().D()) should be refactored into functions
- Keep function signature small
- Use structs instead when data neeeds to be moved around together
- Document areas for improvement
- Document design decisions made, when there are multiple viable options
- Document things that are unsure and should be validated (e.g. possible performance problems)
- Use implicitly typed variables ('var') as much as possible
- Prefer
IReadOnlyListover List, or Array, as an argument - Prefer expression body methods