On Github cvuorinen / clean-code
W3 School 7.4.2014
by Carl Vuorinen / @cvuorinen
Short?
Simple?
Readable?
Self documenting?
Testable?
SOLID?
DRY?
function isMachine($name)
public function isMachine($name) { return str_is($name, gethostname()); }github.com/laravel/framework/blob/master/src/Illuminate/Foundation/EnvironmentDetector.php#L98
public function hostnameMatches($name)
public function isCurrentHost($hostname)"is" beginning implies boolean
State your intentions.
Say what you mean. Mean what you say.
Minimize guess work and possibility for misinterpretation.
- Clear API - Self documentingWhy, not What
$all_permutations = array(array()); foreach ($parameters as $parameter => $values) { $new_permutations = array(); // Iterate over all values of the parameter. foreach ($values as $value) { // Iterate over all existing permutations. foreach ($all_permutations as $permutation) { // Add the new parameter value to existing permutations. $new_permutations[] = $permutation + array($parameter => $value); } } // Replace the old permutations with the new permutations. $all_permutations = $new_permutations; }github.com/drupal/drupal/blob/7.0/modules/simpletest/drupal_web_test_case.php#L615
$all_permutations = array(array()); foreach ($parameters as $parameter => $values) { // To create all possible permutations, // combine current values with all existing permutations $new_permutations = array(); foreach ($values as $value) { foreach ($all_permutations as $permutation) { $new_permutations[] = $permutation + array($parameter => $value); } } $all_permutations = $new_permutations; }
// Use the user's block visibility setting, if necessary. if ($block->custom != BLOCK_CUSTOM_FIXED) { if ($user->uid && isset($user->data['block'][$block->module][$block->delta])) { $enabled = $user->data['block'][$block->module][$block->delta]; } else { $enabled = ($block->custom == BLOCK_CUSTOM_ENABLED); } } else { $enabled = TRUE; }github.com/drupal/drupal/blob/7.x/modules/block/block.module#L783
$enabled = TRUE; if ($block->custom != BLOCK_CUSTOM_FIXED) { $enabled = ($block->custom == BLOCK_CUSTOM_ENABLED); if ($user->uid && isset($user->data['block'][$block->module][$block->delta])) { $enabled = $user->data['block'][$block->module][$block->delta]; } }
// Limit publicly queried post_types to those that are publicly_queryable if ( isset( $this->query_vars['post_type']) ) { $queryable_post_types = get_post_types( array('publicly_queryable' => true) ); if ( ! is_array( $this->query_vars['post_type'] ) ) { if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types ) ) unset( $this->query_vars['post_type'] ); } else { $this->query_vars['post_type'] = array_intersect( $this->query_vars['post_type'], $queryable_post_types ); } }github.com/WordPress/WordPress/blob/master/wp-includes/class-wp.php#L298
if (isset($this->query_vars['post_type'])) { $this->query_vars['post_type'] = $this->limitPubliclyQueriedPostTypes( $this->query_vars['post_type'], get_post_types( array('publicly_queryable' => true) ) ); }
private function limitPublicPostTypes($post_types, array $queryable_post_types) { if (is_array($post_types)) { return array_intersect($post_types, $queryable_post_types); } if (in_array($post_types, $queryable_post_types)) { return $post_types; } return; }
/** * Filter array or string to only those values that are allowed * * @param array|string $values String value or an array of values * @param array $allowed_values Values that are allowed * * @return array|string Value(s) that are allowed */ private function filterArrayOrString($values, array $allowed_values) { if (is_array($values)) { return array_intersect($values, $allowed_values); } if (in_array($values, $allowed_values)) { return $values; } return; }
Unseen control flow makes code harder to follow
preg_match('/^[a-z][a-z\d_]+$/i', $string);
$usernamePattern = "/" . "^[a-z]" // Must begin with a alphabetic character . "[a-z\d_]+$" // Then 1-n alphanumeric characters and/or underscores . "/i"; // case-insensitive preg_match($usernamePattern, $username);- Store regex in vars/constants to give them names. => Reader isn't distracted by the regex.
} elseif (preg_match('/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]* (?:\\\\.[^\'\\\\]*)*)\'/As', $expression, $match, null, $cursor) ) { // strings $tokens[] = new Token( Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1 ); $cursor += strlen($match[0]); } elseif (preg_match('/not in(?=[\s(])|\!\=\=|not(?=[\s(])|and(?=[\s(])|\=\=\=|\>\=| or(?=[\s(])|\<\=|\*\*|\.\.|in(?=[\s(])|&&|\|\||matches|\=\=|\!\=|\*|~|%|\/|\>|\| |\!|\^|&|\+|\<|\-/A', $expression, $match, null, $cursor) ) { // operators $tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1); $cursor += strlen($match[0]); }github.com/symfony/symfony/blob/v2.4.0/src/Symfony/Component/ExpressionLanguage/Lexer.php#L72
Play Golf on your own time
- Professionals write code that others can understandprivate function getSessionId(Response $response) { /** @var Header $cookies */ $cookies = $response->getHeader('Set-Cookie'); return ArrayCollection::create($cookies->parseParams()) ->map( function (array $headerParameters) { return isset($headerParameters['SESSION_ID']) ? $headerParameters['SESSION_ID'] : false; } ) ->find( function ($parameter) { return $parameter; } ); }- Should be commented because not obvious to every dev.
private function getSessionId(Response $response) { /** @var Header $cookies */ $cookies = $response->getHeader('Set-Cookie'); foreach ($cookies->parseParams() as $headerParameters) { if (isset($headerParameters['SESSION_ID'])) { return $headerParameters['SESSION_ID']; } } return false; }
Immutable variables + functions without state = no side effects
- Easier to understand and debug and test.Tests are a form of documentation
They need to be easily readable
What's wrong with code that "just works"?
Code is written once, but read many times.
Adding complexity slows you down tomorrow.
- Easier to test, fix bugs, maintain - Future is the most important thing (most of the time) - Someone (most of the time you) will have to maintain the codeMaintenance vs. Implementation
Does it make sense?
How well can others understand it?
How could this be easier to understand and use?
- Read each line/method/class in isolationStatic code analysis
- Mess detector, pdepend, Jenkins, SonarQubeDon't make assumptions about the future
- Things will change - Make code that is easy to changeBroken window theory
Remove complexity instead of adding it
Hide complexity you cannot remove
- When adding new features, fixing bugs - Design patterns - YAGNI, Remove unused code - Facade (hide complexity)"Code is like humor. When you *have* to explain it, it’s bad" - @housecor
— About Programming (@abt_programming) March 24, 2014“The cheapest time to refactor code is right now.” Great advices by @bugroll http://t.co/GGEQ8Bc3e1 #refactoring #cleancode
— Luca Guidi (@jodosha) March 20, 2014Beautiful code, like beautiful prose, is the result of many small decisions. The right method length here, the right object name there.
— DHH (@dhh) March 21, 2014Your coding standard is in the code. A Word document about your standard says the standard isn't followed #cleancode #agile
— lpcarignan (@lpcarignan) October 25, 2011