Graduate Program KB

Chapter 17 - Smells and Heuristics

List of 'Code Smells' and Heuristics

Comments

  • Inappropriate Information
    • Comments should be reserved for technical notes about the code and design
    • Do not put uninteresting information in comments that should rather be stored in record-keeping or other tracking systems
  • Obsolete Comment
    • Old or irrelevant comments should be updated or deleted
  • Redundant Comment
    • A comment that describes something that adequately describes itself already should be removed
  • Poorly Written Comments
    • Take time to correctly word comments. be clear, concise and omit superfluous information
  • Commented-Out Code
    • Remove 'ghost code' all code that has been commented out

Enviroment

  • Build should not require more than one step or command
  • Running all tests should not require more than one step or command

Functions

  • Limit Arity
    • A function taking zero arguments is best
    • More than 3 parameters should be avoided
  • Avoid Output Arguments
    • Expect arguments to be inputs
    • If a function changes state, have it change the state of the object it is called on
  • Do Not Pass Flag Arguments to Functions
    • If you pass a boolean to a function, it goes against SRP based on the flag
  • Remove Dead Functions
    • methods that are never called should be removed

General

  • Multiple Languages
    • Minimises the number of extra languages in our source files
  • The Principle of Least Surprise
    • All obvious behaviour should be implemented
    • Code should be intuitive for readers and users
  • Check and test for correct behaviour at every boundary condition and corner case
    • Do not rely on your intuition, write a test
  • Do not override safties
    • It may get your code to work or run but at the risk of endless debugging sessions
  • Duplication
    • DRY (Don't Repeat Yourself)
    • Everytime you see duplication in code it is a missed opportunity for abstraction
  • Correctly seperate code based on level of abstraction
  • Base classes should know nothing about their derivatives
  • Modules should have very small interfaces
    • Allow you to do a lot with a little
    • interfaces should hide or limit exposure to their data, utility functions, classes and modules
  • Vertical Separation
    • Variables and functions should be defined close to where they are used
  • Code should be consistent
  • Remove clutter
    • unused variables, functions, comments
  • Artificial Coupling
    • This is the coupling between modules that servers no direct purpose
    • Take time to figure out where functions, constants, variables etc should go
  • Feature Envy
    • Functions should be cohesive and not envy other classes or functions
  • Code should be expressive and show intent
    • Do not use hungarian notation or magic numbers
  • Inappropriate Static Methods
    • In general prefer non static methods over static as we may want a function to be polymorphic
  • Explanatory Variables
    • Break calculations up into intermediate values, held in variables, that explain what the calculation is doing
  • Function names should say what they do
    • Be clear when writting function names
  • Understand the algorithm first before writing code
  • A module should not make assumptions about another module it depends on. Rather we want dependencies to be physical not logical
  • Prefer the use of polymorphism over conditionals (If/Else or Switch/Case)
  • Every team should follow a coding standard based on common industry norms
  • Replace 'Magic numbers' with named constants
  • Do not be lazy. Be precise when writing code.
    • Do not assume your code will do one thing. Write tests for other possible behaviours.
  • Enforce design decisions with structure over conventions.
    • Example: Prioritize the implementation of polymorphism over a switch case with good naming
  • Encapsulate Conditionals
    • Extract functions that explain the intent of the conditional rather than performing logic in the conditional
  • Avoid negative conditionals
  • SRP (Functions should do one thing)
  • Hidden Temporal Coupling (When the order of functions matter but it is not apparent in the code)
    • Expose temporal coupling by having functions return the value te next function needs to execute
  • Don't be arbitrary
    • have a reason for the way you structure your code
  • Encapsulate boundary conditions - All processing should be in one place
  • Functions should descend only one level of abstraction
  • Keep configurable data at high levels
    • constant variables should be at the top of the file
  • Follow the Law of Demeter
    • A module should not know about the inner details of the object it manipulates
  • Avoid long imports by using wildcards
    • If you use two or more classes from a package import the whole package
  • Don't inherit constants
  • Use enums over constants
  • Choose descriptive names
  • Do not pick names that communicate implementation, choose names that reflect the level of abstraction
  • Use standard nomenclature when naming
  • Use Unambiguous Name
  • Names should avoid encodings
  • Names should describe side effects

Tests

  • Insufficient Test
    • Test should cover every aspect of the code
  • Coverage Tools
    • These tools report gaps in your testing strategy
  • Don't skip trivial test
  • An ignored test is a question about ambiguity
  • Take special care to test boundary
  • Bugs tend to congregate, exhaustively test near bugs
  • The patterns from failed test cases and test coverage patterns can reveal the problem
  • Test should be fast
    • If test don't run fast, test wont be run frequently