4. 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.
7. 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?
8. 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
9. 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!
10. WHAT DO WE SPEND TIME ON?
Planning a change
Reading the code
Acting on that plan
Evaluating the outcome
GOTO 10
11. OUR CODE LIKE OUR DATA STORAGE
It has a read:write ratio of 10:1
...and our brains are really bad caches!
12. 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.
13. 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?
14. 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?
15. 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
16. 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.
17. 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.
18. 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
19. 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
21. 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
23. 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
24. 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
25. REUSING CODE
The dependency manager for PHP
As easy as committing everything to your SCM.
But with care free autoloading and updates!
27. 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) {…}
}
28. 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) {…}
}
29. ANOTHER ONE
class Calendar {
public function getMonth($shortened = false) {…}
}
class Calendar {
public function getMonthNames() {…}
public function getShortendMonthNames {…}
}
30. 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.
31. 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
32. 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); // ?
33. 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?
34. 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
35. 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);
}
36. ONLY PUT IN THERE WHAT USERS NEED
interface Logger {
public function log($message);
}
37. FUNCTION NAMING
Centralize implementation but exposing questions, not state
$status = $user->getStatus();
if ($status == $user::STATUS_BANNED) {
}
if ($user->isBanned()) {
}
38. DON'T USE BOOLEANS AS PARAMETERS EITHER
$user->setAdminStatus(false);
$user->setAdminStatus(true);
$user->revokeAdminRights();
$user->grantAdminRights();
39. 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.
40. 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;
}
41. 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, // ...
42. 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;
}
}