- Write automated tests for features, either before or after the 'real' code.
- When fixing any bug, write a test so it can never come back unnoticed.
- (Note that all this testing is not just for having tests; it also encouraged modular design and dependency injection).
- Use linters for style and to find potential bugs.
- Use feature branches that check the tests and linters mentioned above before merging. Reviews are also great, if you're not alone.
- Use types for more than primitives. E.g. make types for Name, Id<Person>, EuroAmt (currency), UnsafeString...
- Write readable code first. Only optimize after measuring performance and writing unit tests.
- Use asserts (or require or whatever) to document assumptions.
- When in doubt, fail. Fast and loud.
- Avoid mutable state where possible:
- Mark object fields final. Maybe all the fields.
- Prefer communicating through arguments and return values rather than changing state.
- Especially prevent sharing mutable state across threads.
- Use immutable collections (many languages don't have them built-in, unfortunately, but find a way).
- Use a language that has non-nullable types.
- Keep units of code as independent as possible, exposing only what they need to know about eachother. Design patterns help here.
- Be willing to throw things out. Schedule time for refactoring. Sometimes it was designed wrong, other times it grew out of control, but it has to be fixed.
- Don't overdo inheritance (no big class trees).
Hashnode is a friendly and inclusive dev community.
Come jump on the bandwagon!
💬 Ask programming questions without being judged
🧠 Stay in the loop and grow your knowledge
🍕 More than 500K developers share programming wisdom here
❤️ Support the growing dev community!