Nicole Carpenter
Web Developer

Clean Code: Chapter 17 - Smells and Heuristics


29 Mar 2016

Code smells are something my mentors here at 8th Light have been warning me about since I started my SnowMan project. I personally do not have the instinctive nose as do they, so I decided that I am going to make a quick cheat sheet with those that are listed in the book.

Code Smell / Heuristic Description Refactoring
     
Comments    
     
Inappropriate information Record keeping or other metadata in source code Keep this type of comment in other systems
     
Obsolete comment Old, irrelevant or incorrect comment Delete it, avoid using comments that will become obsolete (or at all)
     
Redundant comment Comment describes something that would adequately describe itself Omit redundant comments, refactor code to be clear
     
Poorly written comment Comment is not clear about its purpose for writing Write understandable comments, brief and to the point
     
Commented out code Chunks of code not in use but lingering in the source code Delete it, it will become obsolete if left long enough
     
Environment    
     
Build requires more than one step Requiring multiple steps to build Combine setup to a single command
     
Tests require more than one step Requiring multiple calls to run all tests Reorganize files to call back to a spec runner
     
Functions    
     
Too many arguments Three or more arguments on a function Try for zero arguments, add only if necessary
     
Output arguments Function changes something with an output* If the function must change the state of something, have it change the state of its owning object
     
Flag arguments Boolean arguments hint that the function does more than one thing Avoid, break up receiving function to handle two cases
     
Dead function Methods that are not called Delete and don’t write until they are needed
     
General    
     
Multiple languages in one source file Mixing multiple languages in one file Separate languages where possible and practical into their own source file
     
Obvious behavior is unimplemented Code behaves counter-intuitively Descriptive naming that communicates the intent of the code
     
Incorrect behavior at the boundaries Code behaves counter-intuitively for edge cases Test all cases, look for every boundary condition
     
Overridden safeties Turning off warning and exceptions to get the code to build Deal with debugging as it comes up, only override when last resort
     
Duplication Identical (or closely similar) code appearing over and over Replace with better methods, abstractions and polymorphism*
     
Code at wrong level of abstraction High and low level abstractions appear together Separate lower level concepts into derivatives and higher level concepts in the base class
     
Base classes depending on their derivatives Base classes mentioning derivatives Base classes should generally not know about their derivatives
     
Too much information Wide and deep interfaces that require multiple gestures to get things done Limit exposure at interfaces, with fewer methods, variables and instance variables
     
Dead code Code that is not executed Delete dead code if it serves no purpose
     
Vertical Separation Large vertical scope between declaration and use Declare variables and functions close to where they are used
     
Inconsistency Not following a convention Pick a way to do and name things and stick to it
     
Clutter Anything that is not used or not needed in the code Keep code base clean and to the point
     
Artifact coupling Coupling between two modules that serves no direct purpose Take time to figure out where functions, constants and variables belong
     
Feature envy Using accessors and mutators of some other object to manipulate the data within that object Try to have class methods only interact with variables and functions of classes they belong to
     
Selector arguments Passing code to a function to select the behavior Split large functions into smaller functions
     
Obscured intent Code that is hard to read, intent not clear Make intent of code visible to reader
     
Misplaced responsibility Code appears in the wrong class or interfaces Place code where reader would naturally expect it to be
     
Inappropriate static Static function when should be nonstatic Only make a function static if there is no chance it will need to behave polymorphically
     
Use explanatory variables Large calculations with wide span Break calculations up into intermediate values in variables with meaningful names
     
Function names should say what they do You can’t tell from the call what the function does Find better names that express intent
     
Understand the algorithm Code is thrown together that “works” Refactor to a clean and expressive function that is obvious how it works
     
Make logical dependencies physical Dependent modules make assumptions about the module it depends upon Dependent modules should explicitly ask other module for all information it depends upon
     
Prefer polymorphism to If/Else or Switch/Case Use of brute force switch statements Consider polymorphism where you would consider a switch statement
     
Follow standard conventions Not following normal convention, team-specific styles Follow a coding standard based on common industry norms
     
Replace magic numbers with named constants Leaving numbers in code that represent a specific idea Change “magic numbers” to constants
     
Be precise Ambiguities and imprecision in code Make decisions precisely, knowing why the decision was made and how to handle exceptions
     
Structure over convention Poor design decisions Use structures that force compliance
     
Encapsulate conditionals Context is difficult to understand in conditional Extract functions that explain the intent of the conditional
     
Avoid negative conditionals Conditionals expressed as negatives Change methods to return the positive rather than negating a negative
     
Functions should do one thing Function with multiple sections that perform a series of operations Convert large function into several small functions
     
Hidden temporal couplings Order is not obvious for temporal coupling Add extra syntatic complexity to make the temporal coupling explicit
     
Don’t be arbitraty Unorganized code, unclear intent Have a reason for the structure and make sure the structure communicates the reason
     
Encapsulate boundary conditions Boundary conditions leak all over the code Put the processing for boundary conditions in one place
     
Functions should descend only one level of abstraction Mixing levels of abstraction Function statements should be at the same level of abstraction, one level below the operation described by the name of the function
     
Keep configurable data at high levels Constants expected at high level of abstraction buried in low-level function Expose high level constants as arguments to low level function called from high level functions
     
Avoid transitive navigation Single modules know too much about collaborators Make sure modules only know about immediate collaborators and not the navigation map of the whole system
     
Names    
     
Choose descriptive names Names don’t describe intent Make sure name communicates what the purpose of the function or variable is
     
Choose names at the appropriate level of abstraction Names communicate implementation Name things to reflect the level of abstraction of the class or function you are working in
     
Use standard nomenclature where possible Names are not relevant to the project Base names on existing convention or usage
     
Unambiguous names Name does not communicate what the function or variable does/is Choose names that make intent clear and unambiguous
     
Use long names for long scopes Short names that apear over wider scope Reserve single letter variables for small, local scopes
     
Avoid encodings Scope or type information encoded into names Keep names free of pollution
     
Names should describe side-effects Side effects hidden Names should describe everything that a function, variable, or class is or does
     
Tests    
     
Insufficient tests Some conditions remain unexplored by tests or calculations unvalidated Test everything that could possibly break
     
Use a coverage tool Relying on manual tests Use coverage tool with visual indication of coverage
     
Don’t skip trivial tests Not testing small aspects of the code Test everything!
     
An ignored test is a question about ambiguity Not adding a test because something is not understood Add commented out or ignored test to question the requirements
     
Test boundary conditions Not considering all edge cases Consider all boundaries and test them
     
Exhaustively test near bugs Not using extra cauting with testing when a bug is found Test functions that have bugs very exhaustively
     
Patterns of failure are revealing Incomplete test cases poorly organized Create complete test cases to diagnose problems
     
Tests should be fast Slow running tests that won’t get run Keep tests running quickly by whatever means necessary (situations will vary)