1. Use 2 types of objects: Entities and Value Objects
    1. Value Objects are fungible (two objects are equal if their values are equal, regardless of memory address) and immutable
    2. Entities have an identity and are mutable
  2. If info is needed from an object, said object must be present in the constructor (dependency injection, composition > inheritance).
  3. Avoid defining custom exceptions, use custom messages or autogenerated ones unless you can use that error to retry or carry out other actions.
  4. Avoid Primitive Obsession - overusing primitive types instead of defining new, smaller objects
    1. ¿Would any other primitive type make sense here? If not, then use a value object e.g. substituting a float latitude and float longitude for value objects with readonly members and all the necessary checks
  5. Define class invariants (aka limits/conditions for members)
  6. Use named constructors (that use value objects)
  7. Avoid public constants, including setting them in one place with static or singleton. Instead use value wrappers for said constant, e.g. for a constant CRLF you create a class CRLFString which takes care of adding the line break to the string when its constructor is called
  8. Tests shouldn't know anything about the implementation, only that it does a thing or not. Never should you have to make things public just because a tests requires it.
  9. Builders should be nouns (with or without adjectives) and manipulators, verbs.
  10. Composition > Inheritance - Inheritance should be a last resort, and it is generally better to use composition (particularly via dependency injection) than inheritance. That way you can more easily ensure that, whenever you do use inheritance, it respects the LSP.
  11. Tell, don’t ask → command something and the object itself will know how to handle said task. If you ask an object prior, it's feature envy.