SAVE TIME BY APPLYING
CLEAN CODE PRINCIPLES
ABOUT ME
Software Engineer
PHP since 11 years
CI
CleanCode
DevOps
TDD
Shipping
Bullet points
WORKING FOR
ResearchGate gives science back to the people who make it happen.
We help researchers build reputation and accelerate scientific
progress.
On their terms.
GET IN TOUCH
stackoverflow:
Twitter: @_ _edorian
G+ / Xing: Volker Dusch
IRC (freenode): edorian
Mail: php@wallbash.com
AGENDA
Motivation
Big picture
Concrete examples
WE'RE DEVELOPERS
Let's start with us!
We get paid to do what we love
Most of us started because we where fascinated by programming
But what is our job?
OUR JOB IS TO DELIVER
Get things done
Give good estimates
Ask the right questions
Keep doing that
Don't slow down
Keep our promises
THE COST OF HAVING USFOLKSAROUND
German numbers, YMMV €, Approximations
Salary: ~50k a year 50.000€ / Year
adding non-wage labor cost 80.000€ / Year
and office space, water, hardware,
coffee, plants, cleaning, travels, training 100.000€ / Year
Weekends, holidays, vacation, etc:
We work 220 days a year. 455€ / day
"Classic" 8 hour work day 55 Bucks per Hour!
We are expected to contribute over 100.000 Bucks in business value
per year!
WHAT DO WE SPEND TIME ON?
Planning a change
Reading the code
Acting on that plan
Evaluating the outcome
GOTO 10
OUR CODE LIKE OUR DATA STORAGE
It has a read:write ratio of 10:1
...and our brains are really bad caches!
GOALS
Cheap writes
The ability to adapt our software to new requirements.
Cheap reads
The ability to understand and reason about our software quickly.
Writes require reads
There is no UPDATE. All we have is GET and PUT.
CHEAP READS
Your coworkers will read your code again and again and again
Does it take them 2 hours or 5 minutes to understand a module?
SO HOW DO WE KEEP IT EASY?
CHEAP WRITES!
Fear to break something leads to not cleaning up
Not cleaning up leads to expensive reads
Expensive reads lead to expensive writes
So how do we reduce fear of breaking things?
TESTING
TDD is a pragmatic approach to maintainable code.
It's not going to get easier than that ;)
It doesn't even have to take longer
SHORT TDD INTERMISSION
Write unit tests! TDD is just the fastest way, eventually.
Tools? You don't need tools to get started!
watch –n1 'phpunit'and ¼ of a screen is ALL you need!
Aim for 100% test coverage but don't obsess. Test what breaks a lot.
Have a fast test suite. Or use --filterif you can't.
Writing tests can feel like extra work if you are rethinking an already
solved problem.
TDD offers a way to first think about the problem, the interface and
the interactions and then filling in the details step by step until you are
done with the bigger picture.
TESTING ISN'T HARD
Writing proper code is hard
The harder it is to use the code in question, the harder is writing tests
for it
Complex tests mean that the code is to complex. Break it down.
If anything: Mocking is hard(-ish).
Phake is your friend:
http://phake.digitalsandwich.com/docs/html/
FBMock: Mocking for grown ups:
https://github.com/facebook/FBMock
The PHPUnit mocking API is still good enough.
CODING GUIDELINES
A collection for formatting and structure rules so that everyone can
easily find their way around and produce uniform code.
Just create the rule set fitting your practices
Adapt when there is pain
Don't over analyze. Just do it
PHP CodeSniffer:
http://pear.php.net/package/PHP_CodeSniffer
http://pear.php.net/manual/en/package.php.php-
codesniffer.annotated-ruleset.php
http://edorian.github.io/php-coding-standard-generator/#phpcs
COMPLEXITYGUIDELINES
Similar to a coding standard but focusing on hunting down potential
problems within your source code
Possible bugs
Suboptimal code
Overcomplicated expressions
Unused parameters, methods, properties
PHP MessDetector
http://phpmd.org/
http://phpmd.org/rules/
http://edorian.github.io/php-coding-standard-generator/#phpmd
CODE
Rules for structuring our source code that have proven to help with
sustainability.
THE MOST BASIC THING:
Separate the web-glue from the business logic.
Keep templates stupid
Have services owning the logic for manipulation of business entities
Hide the data access behind a layer that you can maintain
individually
SOLID
Single responsibility
Open-closed
Liskov substitution
Interface segregation
Dependency inversion
http://en.wikipedia.org/wiki/SOLID design)
COMPOSITION OVER INHERITANCE
Don't inherit from things if you can just use them.
http://c2.com/cgi/wiki?CompositionInsteadOfInheritance
http://en.wikipedia.org/wiki/Composition_over_inheritance
DEPENDENCYINJECTION
This goes for you code base as well as for your whole Company.
Allows for quick and easy reuse of small components
If wiring is to hard people will copy/paste instead
Can be achieved in many ways. Pick one that works for you
http://pimple.sensiolabs.org/
http://symfony.com/doc/current/components/dependency_injection/index.h
https://github.com/researchgate/injektor
REUSING CODE
The dependency manager for PHP
As easy as committing everything to your SCM.
But with care free autoloading and updates!
CODE EXAMPLES!
What to focus on?
—Phil Karlton
NAMING
A good name tells you everything you need to know!
class User {
public function getId() {…}
public function getName() {…}
/**
* Calculate Body-Mass-Index
* @link ...wikipedia...
* @return float
*/
public function getBMI() {…}
/**
* @param float $kg Weight in kilogramms
*/
public function setWeight($kg) {…}
}
YOU SHOULDN'T NEED COMMENTS
Names are documentation
class User {
public function getUserId() {…}
public function getFirst/Last/Full/DisplayName() {…}
/**
* @return float
*/
public function getBodyMassIndex() {…}
/**
* @param float $kilogramm
*/
public function setWeight($kilogramms) {…}
}
ANOTHER ONE
class Calendar {
public function getMonth($shortened = false) {…}
}
class Calendar {
public function getMonthNames() {…}
public function getShortendMonthNames {…}
}
NAMES ARE GUIDES
Descriptive names communicate intent
They enable us to understand what's up
Misleading names can make it nearly impossible to navigate in a code
base
It is easy to write code that a machine understands.
Writing code that another human can understand is A LOT harder.
CLASSY CLASS NAMES
A class name is the single most important definition of what behavior
fits into that class
Using generic names throws that away!
ApplicationManager, FrameworkDataInformationProvider,
UtilityDataProcessor
ONE CLASS, ONE PURPOSE
Name it after its purpose
There should be only one reason to change a class
A good class names makes inappropriate methods stand out
Do we ask your customers how much they owe us or do we keep track
of that?
$customer->getStoreDiscount(); // ?
$customerManager->getStoreDiscount(); // ?
$discountService->calculateDiscountForCustomer($customer); // ?
SMALLER MORE FOCUSED CLASSES
Should we let our Email class figure out attachment mime types?
By always asking if stuff fits we can "discover" new classes in our
applications
EmailAttachment, EmailImageAttachment?
WHY DO WE CARE AGAIN
Big inflexible classes rob us of all the OOP benefits
Lots of dependencies
Harder to maintain
Harder to reuse
Swapping out a piece of the behavior gets way more complex
INTERFACES ARE FOR THE CONSUMER
interface Logger {
public function log($logMessage);
public function setOutputFormat($format);
public function activate();
public function deactivate();
public function flush();
public function setIncludeTimestamp($format);
public function addLogger(Logger $logger);
}
ONLY PUT IN THERE WHAT USERS NEED
interface Logger {
public function log($message);
}
FUNCTION NAMING
Centralize implementation but exposing questions, not state
$status = $user->getStatus();
if ($status == $user::STATUS_BANNED) {
}
if ($user->isBanned()) {
}
DON'T USE BOOLEANS AS PARAMETERS EITHER
$user->setAdminStatus(false);
$user->setAdminStatus(true);
$user->revokeAdminRights();
$user->grantAdminRights();
FUNCTION LENGTH
Do you like reading functions that are:
100 lines?
20 lines?
7 lines?
Functions tend to get big quickly because it's very easy to write for that
one current specialized use case.
LONG FUNCTIONS
Local variables and code that operates on them? :)
public function log($message) {
$log = '';
$errors = array();
$log .= PHP_EOL . date('Y-m-d H:i:s') . ': ';
if (!$message) {
$errrors[] = 'No Message';
} else {
$log .= $message;
}
if ($fp = !fopen(ERROR_LOG, 'w')) {
$errors[] = 'Error log issue'
}
if (!$fp || !fwrite($this->log)) {
$errors[] = 'Write Error';
}
return $errros;
}
ARRAYS
Arrays are great ad-hoc data structures but hard to read and maintain
$monthlyUsers = array(124334, 123234141, // ...
$monthlyUsers = array(1 => 124334, 2 => 123234141, // ...
$monthlyUsers = array('jan' => 124334, 'feb' => 123234141, // ...
VALUE OBJECTS
class MonthlyValues implements IteratorAggregate {
protected $values = array();
public function __construct(array $values) {
if(count($values) != 12) {
thrown new InvalidArgumentException('...');
}
$this->values = $values;
}
/**
* @param monthNumber See: Month::JANUARY ...
*/
public function getValueFor($monthNumber) {
// error handling...
return $this->values[$monthNumber];
}
public function getIterator() {
return $this->values;
}
}
THANK YOU!
QUESTIONS?
GET IN TOUCH
stackoverflow:
Twitter: @_ _edorian
G+ / Xing: Volker Dusch
IRC (freenode): edorian
Mail: php@wallbash.com
Save time by applying clean code principles

Save time by applying clean code principles

  • 1.
    SAVE TIME BYAPPLYING CLEAN CODE PRINCIPLES
  • 2.
    ABOUT ME Software Engineer PHPsince 11 years CI CleanCode DevOps TDD Shipping Bullet points
  • 4.
    WORKING FOR ResearchGate givesscience back to the people who make it happen. We help researchers build reputation and accelerate scientific progress. On their terms.
  • 5.
    GET IN TOUCH stackoverflow: Twitter:@_ _edorian G+ / Xing: Volker Dusch IRC (freenode): edorian Mail: php@wallbash.com
  • 6.
  • 7.
    WE'RE DEVELOPERS Let's startwith us! We get paid to do what we love Most of us started because we where fascinated by programming But what is our job?
  • 8.
    OUR JOB ISTO DELIVER Get things done Give good estimates Ask the right questions Keep doing that Don't slow down Keep our promises
  • 9.
    THE COST OFHAVING USFOLKSAROUND German numbers, YMMV €, Approximations Salary: ~50k a year 50.000€ / Year adding non-wage labor cost 80.000€ / Year and office space, water, hardware, coffee, plants, cleaning, travels, training 100.000€ / Year Weekends, holidays, vacation, etc: We work 220 days a year. 455€ / day "Classic" 8 hour work day 55 Bucks per Hour! We are expected to contribute over 100.000 Bucks in business value per year!
  • 10.
    WHAT DO WESPEND TIME ON? Planning a change Reading the code Acting on that plan Evaluating the outcome GOTO 10
  • 11.
    OUR CODE LIKEOUR DATA STORAGE It has a read:write ratio of 10:1 ...and our brains are really bad caches!
  • 12.
    GOALS Cheap writes The abilityto adapt our software to new requirements. Cheap reads The ability to understand and reason about our software quickly. Writes require reads There is no UPDATE. All we have is GET and PUT.
  • 13.
    CHEAP READS Your coworkerswill read your code again and again and again Does it take them 2 hours or 5 minutes to understand a module?
  • 14.
    SO HOW DOWE KEEP IT EASY? CHEAP WRITES! Fear to break something leads to not cleaning up Not cleaning up leads to expensive reads Expensive reads lead to expensive writes So how do we reduce fear of breaking things?
  • 15.
    TESTING TDD is apragmatic approach to maintainable code. It's not going to get easier than that ;) It doesn't even have to take longer
  • 16.
    SHORT TDD INTERMISSION Writeunit tests! TDD is just the fastest way, eventually. Tools? You don't need tools to get started! watch –n1 'phpunit'and ¼ of a screen is ALL you need! Aim for 100% test coverage but don't obsess. Test what breaks a lot. Have a fast test suite. Or use --filterif you can't. Writing tests can feel like extra work if you are rethinking an already solved problem. TDD offers a way to first think about the problem, the interface and the interactions and then filling in the details step by step until you are done with the bigger picture.
  • 17.
    TESTING ISN'T HARD Writingproper code is hard The harder it is to use the code in question, the harder is writing tests for it Complex tests mean that the code is to complex. Break it down. If anything: Mocking is hard(-ish). Phake is your friend: http://phake.digitalsandwich.com/docs/html/ FBMock: Mocking for grown ups: https://github.com/facebook/FBMock The PHPUnit mocking API is still good enough.
  • 18.
    CODING GUIDELINES A collectionfor formatting and structure rules so that everyone can easily find their way around and produce uniform code. Just create the rule set fitting your practices Adapt when there is pain Don't over analyze. Just do it PHP CodeSniffer: http://pear.php.net/package/PHP_CodeSniffer http://pear.php.net/manual/en/package.php.php- codesniffer.annotated-ruleset.php http://edorian.github.io/php-coding-standard-generator/#phpcs
  • 19.
    COMPLEXITYGUIDELINES Similar to acoding standard but focusing on hunting down potential problems within your source code Possible bugs Suboptimal code Overcomplicated expressions Unused parameters, methods, properties PHP MessDetector http://phpmd.org/ http://phpmd.org/rules/ http://edorian.github.io/php-coding-standard-generator/#phpmd
  • 20.
    CODE Rules for structuringour source code that have proven to help with sustainability.
  • 21.
    THE MOST BASICTHING: Separate the web-glue from the business logic. Keep templates stupid Have services owning the logic for manipulation of business entities Hide the data access behind a layer that you can maintain individually
  • 22.
    SOLID Single responsibility Open-closed Liskov substitution Interfacesegregation Dependency inversion http://en.wikipedia.org/wiki/SOLID design)
  • 23.
    COMPOSITION OVER INHERITANCE Don'tinherit from things if you can just use them. http://c2.com/cgi/wiki?CompositionInsteadOfInheritance http://en.wikipedia.org/wiki/Composition_over_inheritance
  • 24.
    DEPENDENCYINJECTION This goes foryou code base as well as for your whole Company. Allows for quick and easy reuse of small components If wiring is to hard people will copy/paste instead Can be achieved in many ways. Pick one that works for you http://pimple.sensiolabs.org/ http://symfony.com/doc/current/components/dependency_injection/index.h https://github.com/researchgate/injektor
  • 25.
    REUSING CODE The dependencymanager for PHP As easy as committing everything to your SCM. But with care free autoloading and updates!
  • 26.
    CODE EXAMPLES! What tofocus on? —Phil Karlton
  • 27.
    NAMING A good nametells you everything you need to know! class User { public function getId() {…} public function getName() {…} /** * Calculate Body-Mass-Index * @link ...wikipedia... * @return float */ public function getBMI() {…} /** * @param float $kg Weight in kilogramms */ public function setWeight($kg) {…} }
  • 28.
    YOU SHOULDN'T NEEDCOMMENTS Names are documentation class User { public function getUserId() {…} public function getFirst/Last/Full/DisplayName() {…} /** * @return float */ public function getBodyMassIndex() {…} /** * @param float $kilogramm */ public function setWeight($kilogramms) {…} }
  • 29.
    ANOTHER ONE class Calendar{ public function getMonth($shortened = false) {…} } class Calendar { public function getMonthNames() {…} public function getShortendMonthNames {…} }
  • 30.
    NAMES ARE GUIDES Descriptivenames communicate intent They enable us to understand what's up Misleading names can make it nearly impossible to navigate in a code base It is easy to write code that a machine understands. Writing code that another human can understand is A LOT harder.
  • 31.
    CLASSY CLASS NAMES Aclass name is the single most important definition of what behavior fits into that class Using generic names throws that away! ApplicationManager, FrameworkDataInformationProvider, UtilityDataProcessor
  • 32.
    ONE CLASS, ONEPURPOSE Name it after its purpose There should be only one reason to change a class A good class names makes inappropriate methods stand out Do we ask your customers how much they owe us or do we keep track of that? $customer->getStoreDiscount(); // ? $customerManager->getStoreDiscount(); // ? $discountService->calculateDiscountForCustomer($customer); // ?
  • 33.
    SMALLER MORE FOCUSEDCLASSES Should we let our Email class figure out attachment mime types? By always asking if stuff fits we can "discover" new classes in our applications EmailAttachment, EmailImageAttachment?
  • 34.
    WHY DO WECARE AGAIN Big inflexible classes rob us of all the OOP benefits Lots of dependencies Harder to maintain Harder to reuse Swapping out a piece of the behavior gets way more complex
  • 35.
    INTERFACES ARE FORTHE CONSUMER interface Logger { public function log($logMessage); public function setOutputFormat($format); public function activate(); public function deactivate(); public function flush(); public function setIncludeTimestamp($format); public function addLogger(Logger $logger); }
  • 36.
    ONLY PUT INTHERE WHAT USERS NEED interface Logger { public function log($message); }
  • 37.
    FUNCTION NAMING Centralize implementationbut exposing questions, not state $status = $user->getStatus(); if ($status == $user::STATUS_BANNED) { } if ($user->isBanned()) { }
  • 38.
    DON'T USE BOOLEANSAS PARAMETERS EITHER $user->setAdminStatus(false); $user->setAdminStatus(true); $user->revokeAdminRights(); $user->grantAdminRights();
  • 39.
    FUNCTION LENGTH Do youlike reading functions that are: 100 lines? 20 lines? 7 lines? Functions tend to get big quickly because it's very easy to write for that one current specialized use case.
  • 40.
    LONG FUNCTIONS Local variablesand code that operates on them? :) public function log($message) { $log = ''; $errors = array(); $log .= PHP_EOL . date('Y-m-d H:i:s') . ': '; if (!$message) { $errrors[] = 'No Message'; } else { $log .= $message; } if ($fp = !fopen(ERROR_LOG, 'w')) { $errors[] = 'Error log issue' } if (!$fp || !fwrite($this->log)) { $errors[] = 'Write Error'; } return $errros; }
  • 41.
    ARRAYS Arrays are greatad-hoc data structures but hard to read and maintain $monthlyUsers = array(124334, 123234141, // ... $monthlyUsers = array(1 => 124334, 2 => 123234141, // ... $monthlyUsers = array('jan' => 124334, 'feb' => 123234141, // ...
  • 42.
    VALUE OBJECTS class MonthlyValuesimplements IteratorAggregate { protected $values = array(); public function __construct(array $values) { if(count($values) != 12) { thrown new InvalidArgumentException('...'); } $this->values = $values; } /** * @param monthNumber See: Month::JANUARY ... */ public function getValueFor($monthNumber) { // error handling... return $this->values[$monthNumber]; } public function getIterator() { return $this->values; } }
  • 43.
  • 44.
    QUESTIONS? GET IN TOUCH stackoverflow: Twitter:@_ _edorian G+ / Xing: Volker Dusch IRC (freenode): edorian Mail: php@wallbash.com