Software design principles

  • KISS – Keep it simple and stupid.
  • What You Produce, Others Will Consume
  • Be Open to the Future
  • Plan Ahead for Reuse
  • Clean code minimize the risk of bugs being hidden
  • Loose coupling
  • High cohesion
  • Design should allow local change. Impact of change should be minimal
  • Remove complicated blocks
  • Single responsibility.
  • A class should be small (less than 100 lines of code)
  • Interface segregation principle.
  • Dependency inversion principle
  • Substitution principle.
  • Open close principle.
  • No cycles in package dependency
  • Depend in the direction of stability
  • Stable packages should provide highest abstraction
  • Code build in one step.
  • Test execution in one step
  • Do not ignore warnings.
  • Prefer container managed objects.
  • Keep configurable data at higher levels.
  • Make de-coupled systems. Change in a system shall not impact the other.
  • Avoid complexities.
  • Adding micro layers in the top without addition at the right place.
  • Over configuration. Avoid unnecessary configurations.
  • Any class level variable not defining state of the object need to be pushed into methods as local variables.
  • Have reason for the structure in place. If not, others will feel empowered to change them.
  • Structure over convention.
  • Prefer polymorphism over if-else or switch case statement.
  • Multi threading – Do not mix it with rest of the code. Make them in a seperate class.
  • Misplaced responsibility – Placing the functionality in the wrong place (Do not).
  • Make logical dependencies physical
  • A module should know/call only it’s direct dependencies
  • In a class, if certain methods to be invoked in sequence, enforce it.
  • Avoid artificial coupling. Things that are not meant to be coupled shouldn’t be coupled.
  • A module should know only its direct dependencies.
  • Use dependency injection and container managed object.
  • Choose descriptive names for methods, variables, etc.
  • Name interfaces after functionality
  • Name classes after how they implement the interface functionality
  • Name methods after what they do
  • Naming becomes more important with the scope. Class level fields have higher scope and hence have higher importance to descriptive names
  • Standard nomenclature whereever possible.
  • Follow consistency across codebase wherever applicable.
  • Prefer dedicated value objects than the primitive types.
  • Prefer writing expressive code than writing something very effecient and obscure.
  • A method should do just one thing
  • Method should descend one level of abstraction
  • Prefer few parameters in methods. If ther are many parameters in the method, prefer having a dedicated class
  • If a method change the state of a class, have the state changed by calling a method on which the state is mutated.
  • Avoid methods with boolean flag arguments. Instead overload the method and user more methods
  • If a method is static, make sure that it is appopriate
  • Declare local variables near where it’s been used.
  • Declare private methods near where it’s been used.
  • Prefer to determine packages based on features.
  • Single point of reference. Any thing in the code need to be defined once and referred everhwhere else
  • Prefer positive conditions in code as it improves readability
  • If the conditions are very complex, move it into method.
  • Use checked exception for recoverable errors. Unchecked for irrecoverable errors.
  • Exceptions are costly. Never use it for programming control flow.
  • Never swallow exception in catch block.
  • Declare the specific checked exceptions your method throws. Do not use “throws Exception” but be specific.
  • Do not catch exception class but catch its subclass
  • Never catch throwable because java also throws Throwable
  • While rethrowing, always wrap the exception to get the stack trace.
  • Never log and throw the exception. It will confuse the person who troubleshoots it.
  • Never throw an exception from finally block.
  • Catch exception only if you can handle or provide additional context to it. Otherwise do not catch at all.
  • Throw early catch late. This gives opportunity to handle the exception at the high level abstraction.
  • Use finally to clean up after catching
  • Use exceptions on errors but do not use error codes.
  • Keep the try-catch statement small. Do not put 100 of lines in it.
  • Do not have a try-catch inside a loop.
  • Catch all exceptons before it reaches UI.
  • Always override equals for Value Objects (VO)
  • Constructor can throw exception if the required initialization cannot be done right.
  • Remove all dead code.
  • Remove clutters from the code.
  • Remove all references like story #. Any info which can be stored in a better place.
  • Swallowing exceptions. Swallow only if the excepiton is completely resolved leaving the system in stable state
  • Do not use exceptions to handle control flow in the program.
  • Use exceptions instead of return codes or null
  • Exceptions need to be thrown as early as possible. This helps in getting the right stack.
  • Catch only if you can react to the excepiton in a meaningful way.
  • Be specific in excepitons – catch or throws – be specific
  • Avoid code duplication
  • Replace magic numbers with strings at field level. If a value is of high signification don’t bury it inside.
  • Model interfaces for every role (aspect of the system). Do not model monolithic interfaces.
  • Do not keep configurable item in source code. Move it to configuration file
  • Always override hashcode while overriding equals
  • Very careful while using Cloneable interface as it’s just a marker interface. Think if you can use copy constructor.
  • YAGNI – You aren’t going to need it. Develop only what is required.
  • Make the dependencies tied via fundamental data types if possible.

Leave a Reply

Your email address will not be published. Required fields are marked *