SlideShare a Scribd company logo
1 of 62
Professional-grade
software design
whoami
Brian Fenton
@brianfenton
www.brianfenton.us
Mashery, an Intel Company
Overview
Testing
SOLID
Object
calisthenics
Smells
Patterns
Resources
Professional
Jeff Hebert, HeroMachine.com
Tests!
If you don’t have tests, you’re
REWRITING, not refactoring
Tests first
Testing after you write the class won’t
improve your design
More likely to just test the
implementation (low value, brittle tests)
Unit testing forces you to
feel the pain of bad design
up front
Some smells exposed by
tests
Lots of dependencies
Class/method does too much
Requires lots of setup to do anything
Class is too coupled to its environment
Lots of protected/private methods
Likely another class worth of behavior
hidden inside
SOLID
S – Single Responsibility
O – Open/Closed
L – Liskov Substitution
I – Interface Segregation
D – Dependency Inversion
Because no one can pronounce SRPOCPLSPISPDIP
Single Responsibility Principle
<?php
class Person extends Model {
public $name;
public $birthDate;
protected $preferences;
public function getPreferences() {}
public function save() {}
}
<?php
class Person extends Model {
public $name;
public $birthDate;
protected $preferences;
public function getPreferences() {}
}
class DataStore
public function save(Model $model) {}
}
Open/Closed Principle
Open for extension
Closed for modification
The OCP litmus test
Can you add/change a feature by only adding new
classes?
Also allowed to update
Controllers
Configuration
Templates
Liskov Substitution
Principle
“objects in a program should be
replaceable with instances of their
subtypes without altering the correctness
of that program.”
abstract class Shape{
public function getHeight();
public function setHeight($height);
public function getLength();
public function setLength($length);
}
class Square extends Shape{
protected $size;
public function getHeight() {
return $this->size;
}
public function setHeight($height) {
$this->size = $height;
}
public function getLength() {
return $this->size;
}
public function setLength($length) {
$this->size = $length;
}
}
class Rectangle extends Shape{
protected $height;
protected $length;
public function getHeight() {
return $this->height;
}
public function setHeight($height) {
$this->height= $height;
}
public function getLength() {
return $this->length;
}
public function setLength($length) {
$this->length= $length;
}
}
Interface Segregation Principle
Dependency Inversion
Principle
“Higher level modules should not depend
on lower level modules”
TL;DR
mysqli_query() BAD
DataStore->query() GOOD
Naming
Care about your names
(class/method/variable)
If it’s hard to name something, it means
you can’t describe it succinctly
If you can’t describe what it does, it
does too much or you don’t understand
what it does
Good Naming
Don’t abbreviate
Don’t be afraid to be verbose
Suspect Names
Classes
Manager
Handler
Methods
Process
“And”
Comments
Use inline comments sparingly
Do use docblocks though
TODOs
Tend to rot and never get fixed
If you use a TODO, add a ticket number
A well-named method that
communicates intent is far
more valuable than a comment
Methods
You can (almost) always make a
method smaller
Pay attention to your execution path
Check your CRAP index with phpmd or
codesniffer
You can (almost) always add more
methods
Cognitive load
Declare variables as close to when they
will be used as possible
If you can pass data from method to
method directly, no need for a variable
Source order
Declare methods in the order they’re
called
Public to private
Avoid “magic” values
DRY
Single source of truth
Self-documenting
Which of these is easier to
understand?
json_last_error() == 5;
json_last_error() == JSON_ERROR_UTF8;
$length > 1024
$length > self::MAX_LENGTH
$limit = 0
$limit = self::RATE_UNLIMITED
Not what I mean!
unsigned three = 1;
unsigned five = 5;
unsigned seven = 7;
https://github.com/torvalds/linux/blob/d158fc7f36a25e19791d
25a55da5623399a2644f/fs/ext4/resize.c#L698-700
“2 is a code smell”
- Alex Miller
Dependency Injection
Pass external dependencies into objects
Constructor injection
Setter injection
Potential smell: too many dependencies
Ask for things, don’t look for them
Object Calisthenics
(briefly)
No more than one level of
indentation per method
public function processData($data) {
$newData = array();
$count = 1;
foreach ($data as $row) {
if (!$row) {
continue;
}
if ($count === 1) {
$newData[] = implode(',', array_keys($row));
} else {
$newData[] = implode(',', $row);
}
$count++;
}
return $newData;
}
…
foreach ($data as $row) {
// skip empty rows
if (!$row) {
continue;
}
if ($count === 1) {
$newData[] = implode(',', array_keys($row));
} else {
$newData[] = implode(',', $row);
}
$count++;
}
…
public function processData($data) {
$newData = array();
$count = 1;
$data = $this->filterEmptyRows($data);
foreach ($data as $row) {
if ($count === 1) {
$newData[] = implode(',', array_keys($row));
} else {
$newData[] = implode(',', $row);
}
$count++;
}
return $newData;
}
public function filterEmptyRows($rows) {
return array_filter($rows);
}
public function processData($data) {
$newData = array();
$count = 1;
$data = $this->filterEmptyRows($data);
foreach ($data as $row) {
$newData[] = $this->processRow($row, $count);
$count++;
}
return $newData;
}
public function processRow($row, $count) {
if ($count === 1) {
return implode(',', array_keys($row));
} else {
return implode(',', $row);
}
}
public function processData($data) {
$newData = array();
$headersOnly = true;
$data = $this->filterEmptyRows($data);
foreach ($data as $row) {
$newData[] = $this->processRow($row, $headersOnly);
$headersOnly = false;
}
return $newData;
}
public function processRow($row, $headersOnly) {
if ($headersOnly === true) {
return implode(',', array_keys($row));
} else {
return implode(',', $row);
}
}
Interlude… which is
clearer?
$this->setActive(true);
$this->setActive(false);
OR
$this->activate();
$this->deactivate();
public function processRow($row,
$headersOnly) {
if ($headersOnly === true) {
return $this->getHeaderRow($row);
} else {
return implode(',', $row);
}
}
public function getHeaderRow($row) {
return implode(',', array_keys($row));
}
public function processRow($row,
$headersOnly) {
if ($headersOnly === true) {
return $this->getHeaderRow($row);
} else {
return $this->toCsv($row);
}
}
public function toCsv($row) {
return implode(',', $row);
}
public function processData($data) {
$newData = array();
$data = $this->filterEmptyRows($data);
$firstRow= array_pop($data);
$newData[] = $this->getHeaderRow($firstRow);
foreach ($data as $row) {
$newData[] = $this->toCsv($row);
}
return $newData;
}
public function transformToCsv($data) {
$data = $this->filterEmptyRows($data);
$firstRow= array_pop($data);
$csv = array();
$csv[] = $this->getHeaderRow($firstRow);
foreach ($data as $row) {
$csv[] = $this->toCsv($row);
}
return $csv;
}
public function processData($data) {
$newData = array();
$count = 1;
foreach ($data as $row) {
if (!$row) {
continue;
}
if ($count === 1) {
$newData[] = implode(',', array_keys($row));
} else {
$newData[] = implode(',', $row);
}
$count++;
}
return $newData;
}
Don’t use else
public function addThreeInts($first, $second, $third) {
if (is_int($first)) {
if (is_int($second)) {
if (is_int($third)) {
$sum = $first + $second + $third;
} else {
return null;
}
} else {
return null;
}
} else {
return null;
}
return $sum;
}
public function addThreeInts($first, $second, $third) {
if (!is_int($first)) {
return null;
}
if (!is_int($second)) {
return null;
}
if (!is_int($third)) {
return null;
}
return $first + $second + $third;
}
Command-Query
Separation
Complete separation between questions
and commands
“Asking a question shouldn’t change the
answer”
public function getUser($id) {
$user = $this->dataStore->fetchUser($id);
if (!$user) {
$user = new User(array($id));
}
return $user;
}
public function getUser($id) {
return ($this->dataStore->fetchUser($id) ?: null;
}
The final secret…
OOP is all about message
passing and behaviors
It’s not about inheritance
It’s not about code reuse
Favor composition over inheritance
Treat objects like APIs
User
Event
Listener
Mailer
"Send
Notification"
w/User data
So decouple!
Such architecture
So amaze
Wow
Much message
Summary
Write small objects
Write tiny methods
Strive for good names
Seek loose coupling
Focus on message passing
Treat objects like APIs
Write tests (first)
Refactor w/discipline
Limit nesting/no else
Use guard clauses
Avoid magic (anything)
Use CQS
Avoid in-line comments
Reduce cognitive load
Resources - Presentations
The Clean Code Talks -- Inheritance,
Polymorphism, & Testing
Object Calisthenics
Resources – Books
Code Complete: A Practical Handbook of
Software Construction, Second Edition
Growing Object-Oriented Software, Guided
by Tests
Refactoring: Improving the Design of Existing
Code
Questions?
https://joind.in/10562

More Related Content

What's hot

How to Become a Tree Hugger: Random Forests and Predictive Modeling for Devel...
How to Become a Tree Hugger: Random Forests and Predictive Modeling for Devel...How to Become a Tree Hugger: Random Forests and Predictive Modeling for Devel...
How to Become a Tree Hugger: Random Forests and Predictive Modeling for Devel...Matt Harrison
 
Object Oriented Programming in PHP
Object Oriented Programming in PHPObject Oriented Programming in PHP
Object Oriented Programming in PHPLorna Mitchell
 
Analysis of Fatal Utah Avalanches with Python. From Scraping, Analysis, to In...
Analysis of Fatal Utah Avalanches with Python. From Scraping, Analysis, to In...Analysis of Fatal Utah Avalanches with Python. From Scraping, Analysis, to In...
Analysis of Fatal Utah Avalanches with Python. From Scraping, Analysis, to In...Matt Harrison
 
PHP Functions & Arrays
PHP Functions & ArraysPHP Functions & Arrays
PHP Functions & ArraysHenry Osborne
 
Class 8 - Database Programming
Class 8 - Database ProgrammingClass 8 - Database Programming
Class 8 - Database ProgrammingAhmed Swilam
 
Simple class and object examples in java
Simple class and object examples in javaSimple class and object examples in java
Simple class and object examples in javaHarish Gyanani
 
Twig Brief, Tips&Tricks
Twig Brief, Tips&TricksTwig Brief, Tips&Tricks
Twig Brief, Tips&TricksAndrei Burian
 
The Ring programming language version 1.7 book - Part 35 of 196
The Ring programming language version 1.7 book - Part 35 of 196The Ring programming language version 1.7 book - Part 35 of 196
The Ring programming language version 1.7 book - Part 35 of 196Mahmoud Samir Fayed
 
Slides chapter3part1 ruby-forjavaprogrammers
Slides chapter3part1 ruby-forjavaprogrammersSlides chapter3part1 ruby-forjavaprogrammers
Slides chapter3part1 ruby-forjavaprogrammersGiovanni924
 
Exploring type level programming in Scala
Exploring type level programming in ScalaExploring type level programming in Scala
Exploring type level programming in ScalaJorge Vásquez
 

What's hot (20)

How to Become a Tree Hugger: Random Forests and Predictive Modeling for Devel...
How to Become a Tree Hugger: Random Forests and Predictive Modeling for Devel...How to Become a Tree Hugger: Random Forests and Predictive Modeling for Devel...
How to Become a Tree Hugger: Random Forests and Predictive Modeling for Devel...
 
Object Oriented Programming in PHP
Object Oriented Programming in PHPObject Oriented Programming in PHP
Object Oriented Programming in PHP
 
Lecture 7 arrays
Lecture   7 arraysLecture   7 arrays
Lecture 7 arrays
 
Analysis of Fatal Utah Avalanches with Python. From Scraping, Analysis, to In...
Analysis of Fatal Utah Avalanches with Python. From Scraping, Analysis, to In...Analysis of Fatal Utah Avalanches with Python. From Scraping, Analysis, to In...
Analysis of Fatal Utah Avalanches with Python. From Scraping, Analysis, to In...
 
PHP Functions & Arrays
PHP Functions & ArraysPHP Functions & Arrays
PHP Functions & Arrays
 
Metaprogramming
MetaprogrammingMetaprogramming
Metaprogramming
 
Python programming : Classes objects
Python programming : Classes objectsPython programming : Classes objects
Python programming : Classes objects
 
Class 8 - Database Programming
Class 8 - Database ProgrammingClass 8 - Database Programming
Class 8 - Database Programming
 
L03 Software Design
L03 Software DesignL03 Software Design
L03 Software Design
 
PHP- Introduction to Object Oriented PHP
PHP-  Introduction to Object Oriented PHPPHP-  Introduction to Object Oriented PHP
PHP- Introduction to Object Oriented PHP
 
Sorting arrays in PHP
Sorting arrays in PHPSorting arrays in PHP
Sorting arrays in PHP
 
Xm lparsers
Xm lparsersXm lparsers
Xm lparsers
 
Simple class and object examples in java
Simple class and object examples in javaSimple class and object examples in java
Simple class and object examples in java
 
Python programming : Inheritance and polymorphism
Python programming : Inheritance and polymorphismPython programming : Inheritance and polymorphism
Python programming : Inheritance and polymorphism
 
Java for beginners
Java for beginnersJava for beginners
Java for beginners
 
Oop concepts in python
Oop concepts in pythonOop concepts in python
Oop concepts in python
 
Twig Brief, Tips&Tricks
Twig Brief, Tips&TricksTwig Brief, Tips&Tricks
Twig Brief, Tips&Tricks
 
The Ring programming language version 1.7 book - Part 35 of 196
The Ring programming language version 1.7 book - Part 35 of 196The Ring programming language version 1.7 book - Part 35 of 196
The Ring programming language version 1.7 book - Part 35 of 196
 
Slides chapter3part1 ruby-forjavaprogrammers
Slides chapter3part1 ruby-forjavaprogrammersSlides chapter3part1 ruby-forjavaprogrammers
Slides chapter3part1 ruby-forjavaprogrammers
 
Exploring type level programming in Scala
Exploring type level programming in ScalaExploring type level programming in Scala
Exploring type level programming in Scala
 

Similar to Professional-grade software design

Objects, Testing, and Responsibility
Objects, Testing, and ResponsibilityObjects, Testing, and Responsibility
Objects, Testing, and Responsibilitymachuga
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11Michelangelo van Dam
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxMichelangelo van Dam
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overviewjsmith92
 
Demystifying Object-Oriented Programming - Lone Star PHP
Demystifying Object-Oriented Programming - Lone Star PHPDemystifying Object-Oriented Programming - Lone Star PHP
Demystifying Object-Oriented Programming - Lone Star PHPAlena Holligan
 
Demystifying Object-Oriented Programming - ZendCon 2016
Demystifying Object-Oriented Programming - ZendCon 2016Demystifying Object-Oriented Programming - ZendCon 2016
Demystifying Object-Oriented Programming - ZendCon 2016Alena Holligan
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosDivante
 
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
4Developers 2015: Be pragmatic, be SOLID - Krzysztof MenżykPROIDEA
 
Building a horizontally scalable API in php
Building a horizontally scalable API in phpBuilding a horizontally scalable API in php
Building a horizontally scalable API in phpWade Womersley
 
Working with databases in Perl
Working with databases in PerlWorking with databases in Perl
Working with databases in PerlLaurent Dami
 

Similar to Professional-grade software design (20)

Objects, Testing, and Responsibility
Objects, Testing, and ResponsibilityObjects, Testing, and Responsibility
Objects, Testing, and Responsibility
 
Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework apps
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
Drupal7 dbtng
Drupal7  dbtngDrupal7  dbtng
Drupal7 dbtng
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
 
PHP 5 Boot Camp
PHP 5 Boot CampPHP 5 Boot Camp
PHP 5 Boot Camp
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
 
Demystifying Object-Oriented Programming - Lone Star PHP
Demystifying Object-Oriented Programming - Lone Star PHPDemystifying Object-Oriented Programming - Lone Star PHP
Demystifying Object-Oriented Programming - Lone Star PHP
 
Demystifying Object-Oriented Programming - ZendCon 2016
Demystifying Object-Oriented Programming - ZendCon 2016Demystifying Object-Oriented Programming - ZendCon 2016
Demystifying Object-Oriented Programming - ZendCon 2016
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenarios
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Scala introduction
Scala introductionScala introduction
Scala introduction
 
Be pragmatic, be SOLID
Be pragmatic, be SOLIDBe pragmatic, be SOLID
Be pragmatic, be SOLID
 
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
 
Building a horizontally scalable API in php
Building a horizontally scalable API in phpBuilding a horizontally scalable API in php
Building a horizontally scalable API in php
 
OOP
OOPOOP
OOP
 
OOP in PHP.pptx
OOP in PHP.pptxOOP in PHP.pptx
OOP in PHP.pptx
 
Working with databases in Perl
Working with databases in PerlWorking with databases in Perl
Working with databases in Perl
 
PHP Unit Testing
PHP Unit TestingPHP Unit Testing
PHP Unit Testing
 
Moodle Quick Forms
Moodle Quick FormsMoodle Quick Forms
Moodle Quick Forms
 

Recently uploaded

The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...Wes McKinney
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsRavi Sanghani
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfIngrid Airi González
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditSkynet Technologies
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rick Flair
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfpanagenda
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 

Recently uploaded (20)

The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and Insights
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdf
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance Audit
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...Rise of the Machines: Known As Drones...
Rise of the Machines: Known As Drones...
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 

Professional-grade software design

Editor's Notes

  1. Can everyone hear me? My name is Brian Fenton and I’ll be talking about Software Design. I’ll leave some time for questions at the end, but if you have a question during the talk or think I’m going too quickly, feel free to raise your hand, start shouting obscenities, throw a shoe at me, whatever. If I don’t know the answer come find me afterwards and we’ll look it up, so that way we both learn something. Let’s get started.
  2. Just a little bit of information about me and where you can find me. I work at Mashery, and we are, of course, hiring.
  3. So during this talk I’m going to go over testing, a fair bit on SOLID and object calisthenics. Most of the smells and patterns stuff will be interspersed with other sections, and I’m not really talking much about the resources other than to provide some avenues for further learning.
  4. This talk is NOT on how to be arockstar cowboy ninja coder, this is how to produce clean, maintainable software that’s easy to modify and maintain over months and years of use. If you’re here to write crappy startup code that you can keep afloat by throwing more AWS instances at it until you get bought, this isn’t the talk for you. Being a professional is not easy. It takes a significant amount of discipline. It involves refactoring by the book and only updating the code via small, repeatable, verifiable steps. As soon as you start a refactoring, you’re not changing anything other than that tiny piece anymore until you’re done. One change at a time.Being professional also involves sticking to your principles. It means taking the time to verify functionality, writing the tests, writing good commit messages, even when you’re under deadline pressure. The real measure of someone’s professionalism is whether or not they stick with the best practices even when it’s not convenient.
  5. This talk is about design, but a lot of the things I’ll mention have to do with testing as well. Testing will be a minor running theme throughout the talk, but since there are two or three other testing-related talks at this event I’ll leave the deep discussion on it to them. You can have the greatest design in the world, but if it the code doesn’t actually do what it’s intended to do, it’s at best useless, or at worst outright dangerous. Suffice to say for now that tests are very important and they can be a great tool to influence, verify, and safely evolve your design.
  6. Making these changes in a system that’s already been released is like operating without a safety net, unless you have good test coverage to protect you from making unnoticed mistakes. If you can’t cheaply prove that the system behaves one way before you start, how can you show that it still behaves that way when you’re done? If you’re calling what you’re doing refactoring but you don’t have tests, you’re just lying to yourself.
  7. If you’ve had the chance to see a set of tests written before the code and one written after the code was finished, they’re strikingly different. The “after” tests are much more concerned with the implementation details of the class and making sure they have good line coverage, whereas the “before” tests are more about verifying the desired external behavior. That’s really what we care about with unit tests anyway, is making sure the class exhibits the right behavior. Implementation-focused tests actually make refactoring harder because they break if the internals of the classes change, and you’ve just cost yourself the information hiding benefits of OOP.
  8. When you’re writing unit tests, you’re forcing yourself to actually use the class to accomplish things. If you write tests at the end, or worse yet, just chuck the code over the wall for QA or whoever to write tests, you don’t get any feedback about how the class actually behaves. If you’re writing tests, and the class is a real pain in the ass to use, you’re going to find out while you’re writing it, which is about the cheapest time to fix it.
  9. If a class is hard to test, it’s a design flaw. Different flaws manifest themselves in different ways, though. If you have to do a ton of mocking, your class probably has too many dependencies, or your methods are doing too much. The more setup you have to do for each test, the more likely it is that your methods are doing too much. If you have to write really convoluted test scenarios in order to exercise behavior, the class’s methods are probably doing too much. If you have to dig inside a bunch of private methods and state to test things, maybe there’s another class trying to get out. Unit testing is very good at exposing “iceberg classes” where 80% of what the class does is hidden away in protected or private code. I used to be a big fan of making as much as possible protected, but now I realized I was just making my individual classes responsible for too much.
  10. I’m going to list these fairly quickly but don’t worry, I have a number of overly contrived examples at the ready
  11. Classes should have only one responsibility. I’d call this one of the most important of the five principles. You see in this picture the cat appears to be doing an excellent job of catching rats, but that’s all he’s doing. And that’s totally fine. This fits nicely with the Unix philosophy of lots of small tools, doing one thing well. Classes that only do one thing are much easier to test and debug, and they are less likely to surprise you. You don’t want a method call to a Validator class updating db records.
  12. So this is a pretty basic entity model. One of these things doesn’t belong here though. A model’s only responsibility should be behavior related to the entity it’s representing, it shouldn’t be responsible for persisting itself. Yes, this makes ActiveRecord a suboptimal pattern, and I’ll happily rant about that later if anyone wants to listen.
  13. This is better. The Person model is back to only doing one thing, and the save behavior has been moved to a persistence object instead. Note also that I only type hinted on Model, not Person. I’ll come back to that when we get to the L part of SOLID in a few slides.
  14. I’m not actually going to do a cat for all of these, sorry. I thought about it but it just didn’t work out. Anyway, the open closed principle states that an object should be OPEN for extension but CLOSED for modification. That’s easy to say, but what does it mean?
  15. There’s an awesome test for this that pretty well sums up what this principle is about: think of a feature to implement, probably the most recent one you worked on or are working on. Can you implement that feature in your codebase SOLELY by adding new classes and not changing any existing classes in your system? Your configuration and wiring code gets a bit of a pass, but in most systems this is surprisingly difficult. You have to rely a lot on polymorphic dispatch and most codebases just aren’t set up for that. If you’re interested in that there’s a good Google talk up on youtube about writing code without Ifs that digs into it further.I’m generally a big fan of class constants but this is one place that they can bite you. If you’re using a constant value you can’t override it in a child class, so if there’s any chance it might actually vary, use a regular property instead.
  16. Objects of a certain type or subtype should be transparently swappable. Named after Barbara Liskov. A very simple example of this is with shapes
  17. This is going to represent our basic four-sided shape. Nothing fancy here.
  18. Here’s our first shape, the Square. Pretty straightforward shape, right? You can assume that there’s a constructor where we set the dimensions, but you see here from this implementation that the length and height are always going to be the same. Squares are just like that.
  19. So here we have a different shape. Still has the same method signatures, it’s still a four sided shape, but what if we start trying to use them in place of one another? Now all of a sudden if we change the height of our Shape, we can no longer assume that the length of our shape will match. We’ve violated the contract that we had with the user when we gave them our Square shape. This is a textbook example of a violation of the LSP and we need this type of a principle in place to make the best use of a type system. Even duck typing won’t tell us if the underlying behavior is different, and since we can’t know that without seeing it break, it’s best to make sure that it isn’t different in the first place.
  20. Back to cats. This principle says to favor many small, fine grained interfaces vs. one large one. Interfaces should be based on behavior rather than “it’s one of these classes”. Think of interfaces that come with PHP. Traversable, Countable, Serializable, things like that. They advertise capabilities that the object possesses, not what it inherits from. So keep your interfaces small. You don’t want an interface to have 30 methods on it, 3 is a much better goal.
  21. You’ve probably heard about this in other places that talked about Dependency Injection, but Dependency Inversion and Dependency Injection aren’t quite the same thing. Dependency inversion is really just a way of saying that you should depend on abstractions in your system and not on its details. Now what does that mean to you on a day to day basis?
  22. The core of this principle is actually about abstractions. It’s more about saying “use a database adapter” instead of depending on direct calls to things like mysql_query. If you’re directly using mysql_query in half your classes then you’re tying everything directly to your database. Nothing for or against MySQL here, but if you are using mysql_query, that type of low level detail should be hidden away in only one place and then that functionality should be exposed via a generic wrapper. Now I know this is kind of a hackneyed example if you think about it, because the number of times you’re going to actually completely change your database engine after your product is in production are very, very low. I picked it because I figured people would be familiar with the idea from their own code. Also, even if you have a database that you know you’re sticking with, that more abstract wrapper object allows you to fix bugs, change behavior, or implement features that you wish your chosen database had. It also makes unit testing possible where low level calls wouldn’t.
  23. Naming is a leading indicator of poor design in kind of the same way unit testing can be. If you’re having a hard time naming a thing then there’s a decent chance you don’t truly understand it. Names are like magic… if you know something’s true name, then you have power over it. If you really can’t come up with a name for something, try splitting it into smaller pieces. Grab a thesaurus. Grab a coworker. Run them through the method and ask them what they’d call it. The best names are often a collaborative effort.
  24. These are really just two ways of saying the same thing. Use as many characters as you need to express the concept. There’s no excuse to have a variable named $x unless you’re plotting coordinates. Unless you’re using a loop counter or something where there’s a well-known pattern to follow, single character variables are just unhelpful. If you’re worried about typing out long variable names all the time, realize that you’re probably only going to type a few characters of it before autocomplete kicks in. I’m not going to say that all of these are 100% bad all the time, but they’re kind of the lazy way out. And not the good kind of programmer lazy that we all value, the regular lazy that gets you into trouble down the road.
  25. These are just a few examples, there are plenty more, but the usually follow the same patterns. Manager is really too generic most of the time. Handler is the same way. Think of what the class actually does, you may really want something like Converter or Transformer or Storage or Parser. A lot of the time if you have something like a UserManager, that class is really a controller instead. “Process” is kind of seductive because it sounds like it’s a very succinct, clear way to describe something, but the problem is it’s still doesn’t impart useful meaning. Think about an unfortunately common method name like “processData”. What does that method called really do? I mean, sure, it processes data, but how? What does it actually do to it? What does the data look like when it’s done? Is “data” the most specific we can be? “And” is an easy one, that almost always means a method is doing too much. I do make an exception with And if you’re naming test methods though.
  26. The best reason to use a comment that I can think of is if you did something unintuitive but valid, like unrolling a loop for performance reasons. That’s the type of information you can’t really encapsulate into a named method call very easily. Another thing I see occasionally is a comment like “doA must be called before doB”. That’s an example of Temporal Coupling, and if that’s the case, just wrap both of those calls up into a new method. That’s another place you might consider using “and” in a method name. Tiny methods grouped together into a progression also opens up a pattern called “Template Method”. What you have there is a method that does one discrete thing, but you pull out a tiny portion of it into its own method just so a child class can change its behavior.
  27. I like putting todos in the code, but I’m not always very good at following up on them right away. If that’s the case, all it’s really doing is adding noise. If you do use todo comments, make sure to reference a ticket in an issue tracker. That way you know when the comment has “expired”, or if it’s already been done, it provides a very concise pointer to context surrounding a decision.
  28. When you see inlinecomments in your code, think about whether you can just replace the commented code with a method that makes the comment unnecessary. Comments rot much faster than code does. If your code needs that many comments, refactor it until its purpose and meaning is self-apparent.
  29. I’m not going to read this to you but keep your methods simple, ideally linear, with as little branching as possible. Fewer branches means easier testing. The only time I really wouldn’t add a new method is if all you’re doing is calling a known and well-understood PHP function inside of it. I wouldn’t write a method to increment an integer, or reverse a string, UNLESS adding that method allowed me to clarify the purpose behind the call. If what I was doing was formatting a string so it could be read in a mirror, I’d probably make a method for that, even if all I was doing was reversing a string, because it tells you why. You can always learn what’s happening in a piece of code if you dig enough, but the why is much more important. If all you have is what the code does, you don’t know if what it’s doing is actually correct or a bug. The reason that second almost is up there is because after a certain point you probably want to make a new collaborator object that handles part of the class’s behavior. Having a ton of methods is a pretty good sign that the class is doing too much.
  30. Don’t always declare all your variables at the top. This isn’t JavaScript, they won’t be hoisted. If you declare a variable at the top of your method and only use it near the bottom, your brain has to keep track of whatever happens to that variable throughout the entire method instead of just for a line or two at the end. Make things easier on yourself and just declare things right when you need them.That second bullet is a bit of a functional programming thing, but it’s just another way of avoiding dealing with system state if you don’t have to. Only add intermediate variables if the syntax requires it like preg_match, or if it makes the intent of the code more clear
  31. I’m advocating for readable code here. I want to read source like you’d read a novel: a method, then methods that are called within it, then the next method, etc… organize your code into paragraphs of related methods, large to small, public to private. If you have to jump back and forth all over a class to trace the execution, take a few minutes and reorder things. It makes for ugly diffs, but it’s easy to maintain once it’s done.
  32. So when I talk about a magic value here, what I mean is a string or integer that has a special meaning. If you ever have to change one of those values, you’re stuck hunting all the way through your code base trying to find all the places you use those values and hope you don’t miss any or have a false positive and change something by mistake. Instead you just have a pointer that references that single source of truth and you can update them all in one go.
  33. I prefer class constants here instead of global constants, because there are fewer risks of name collisions and there’s automatic context provided by attaching the value to an object. Pay special attention to the last one because that’s particularly tricky to figure out what the author meant without the constant. Is it actually a limit of zero, meaning whatever that is rejects everything, or does it mean there is no limit at all? With the constant that becomes immediately apparent.
  34. This can still go wrong. This is actual code in the ext4 filesystem. I don’t want to be the person who has to debug that.
  35. Any time you see an integer in your codebase other than 0 or 1, you should stop and think about it. Does it have special meaning? Are you just doing math? In the vast majority of systems, the only meaningful numbers are 0, 1, and “many”. If you need more conceptual values than that, you should probably give them names.
  36. More dependencies usually mean the class is too large or doing too much. If your class has 4 or 5 dependencies, take a good look and see if all of them are being used and if you shouldn’t extract that behavior to a collaborator object instead. Say you have a User object that, as part of its job, triggers a notification email when someone registers. Now conceivably you could have a mail message object, a mime type object, a template renderer, a mail transport object, or maybe a queue of some kind. Now whatever sort of structure you have set up for your emails, there’s no reason why that original User object needs to have all of those objects as dependencies. It just wants to send a notification, it shouldn’t care about the template renderer, the underlying mail transport, any of that, it should just have the ability to queue up a notification.
  37. This is the topic of a presentation that Raphael Dohms has given a number of times in different venues. I’m not here to re-give his presentation on that, so I’m going to just cover a the first two guidelines to practice. They’re easy to remember, provide good value, and aren’t touched on as much elsewhere in this talk.
  38. This is a helpful way to think about decomposing methods into smaller chunks, leaving you with code that’s clearer and more self-documenting. The more levels of indentation you have, the more the method is doing and the more state you have to keep track of in your head while you’re working with it. I’m also not saying you shouldn’t indent the code to show it’s part of a method, that one’s free.
  39. Ok, so here we have a relatively straightforward piece of code. It should be pretty clear what this method does after a read or two but it can definitely be made more clear. A quick count shows we have two levels of indentation inside the method, one on the foreach and a second one in a few other places inside it. So let’s just look at the code inside the foreach to start. The first thing we’re doing is skipping over empty rows, and for that we don’t even need to be part of the loop at all, we can just use an existing built-in PHP function.
  40. The first thing we’re doing is skipping over empty rows, and for that we don’t even need to be part of the loop at all, we can just use an existing built-in PHP function.
  41. So here we’ve removed one area where we have a second level of nesting, and we have a clearly named method that explains exactly what’s going on. There is nothing wrong with a one line method, and now we have an easy place to change if we need to update the filtering logic
  42. In this step we’ve taken the inner block of the foreach and extracted it into its own method. We now fit our rule about only one level of nesting, but it’s pretty obvious here that count parameter isn’t much more than a boolean flag
  43. Here we renamed the $count parameter to the more fitting $headersOnly and made it a real boolean. Note that none of these changes have yet changed any of the behavior of the system. At each step we’ve maintained consistent output, and each step continues to make the intent clearer and the logical path simpler.However, passing a boolean flag to a method is generally a code smell.
  44. Many times when you’re dealing with a boolean flag to a method there’s a clearer way to represent it. Whenever I see a boolean flag being passed to a method I always suspect that it should really be two methods. It’s either toggling some sort of state like the example above, or it’s delegating to two different code paths internally and they can be split out. I’m not saying you can’t still have a setActive private method to handle those transitions, but don’t make it part of the class’s public API
  45. So now that we’ve gotten that out of the way, let’s get rid of that boolean parameter. We can do an Extract Method refactor on part of processRow, and now we’ve added another descriptive name.
  46. We can another Extract Method on the other half lets us nets us another descriptive name…
  47. We now no longer get much use out of that processRow function, so let’s get rid of it and call those two new methods we created instead. In doing that, the smelly boolean flag has been removed. To do that we have to extract the logic to process the first row separately out of the loop. Remember we’re running our tests after every one of these changes so we have no breakages.
  48. Now we’re pretty much finished. We’ve renamed the original method and the newData parameter to make it clear that we’re talking about CSVs, and we’ve reduced our cognitive load by clustering the code into related sections and defining our variables close to their first use. All these new methods that we created are simple to test, and the original method became less complex and easier to follow.
  49. Just a step back to show what we started with. It’s now a lot more clear what the code is supposed to do
  50. This really deals with two main ideas. The first one is multiple return statements from a method. If you have enough information do make a decision about the method’s result, go ahead and return it then. The second is an idea known as Guard Clauses. These are basically validation checks combined with early returns, usually near the top of a method. Let me show you what I mean.
  51. So this is pretty straightforward again, it adds 3 ints together and returns the result or null if any of the parameters are not an int. Ignoring the fact that we could combine all those checks onto a single line with AND operators, I think you can see how the nested if/else structure makes the code harder to follow. Now look at this example instead.
  52. To me this example is much easier to follow. Here we’re using guard clauses to verify our initial assertions about the parameters we’re passing and immediately exiting the method if they don’t pass. We also no longer have the intermediate variable to track the sum all the way through the method. In this case we’ve verified that we’re already on the happy path and we can just do what we came here to do. Again we could just do all those checks in one IF but the same principle applies.
  53. This is one of those practices that takes real discipline, but over time can avoid a large number of bugs. The basic idea is to make a hard separation between any methods that query the system for data in any way, from simple object getters to database queries to API calls, and methods that send commands to the system to change its state in some way.Another way of looking at it is in terms of HTTP semantics. Queries are your GET calls, they should be idempotent. Other calls can change the system.
  54. So this is a typical example that you might see that violates the principles of CQS. We’re attempting to get a user by ID, but if that user doesn’t exist in the system, we’re modifying the system to create a new user. In this case you can just allow the getUser method to return null and check that in the calling code. Move the new user creation there if it’s necessary.
  55. This kind of relates to one of the guidelines for API design too… be permissive in what you accept, but strict in what you return. If this was a GET call to a /users endpoint, we wouldn’t expect a formerly non-existent user to come back, we’d expect an empty response or a 404 or something. The more you design your system as discrete services communicating with each other over clearly-defined boundaries, the easier it will be to test and modify. It IS possible to go a little overboard with CQS. Entire frameworks exist for this that define Command objects, Query objects, and set up very strict boundaries between different sections. That’s going to be overkill for all but the largest projects. I know we’re not building Java here, I just bring up this pattern to use as another good guideline for design. It will save you from bugs introduced when the system state changes unexpectedly. You may notice me saying this a lot. State is where bugs live.
  56. Are you all ready? This is the big one…
  57. A final theme I wanted to be sure and mention is around the fundamental idea of object-oriented programming. Most of us start out thinking that objects are a way of getting code reuse without copy and paste. That’s fine, and that’s one purpose that objects serve. That’s not the whole picture though, and thinking about them in that way makes inheritance the major focus. Objects are more than just a combination of state and behavior. Try to treat your objects like they’re each an external computer that you can only communicate with in very specific ways. You’ll be amazed at how tightly coupled your systems suddenly appear if you start to think this way, and how flexible they can really be.That earlier example with a User object sending a notification? What if we didn’t even consider it sending the notification itself, and instead we thought about it in terms of messaging. What would that look like?
  58. So as you can see, in this example, there are a lot of objects in play, but more importantly, none of them really have any knowledge or care about any of the others. Each one only has one main purpose, and it receives messages and exhibits behavior based on that. If this looks suspiciously like JavaScript, keep in mind that there are tools like this for PHP as well, such as the Symfony2 EventListener. Just because one language did something right doesn’t mean you can’t import that behavior into a different language.