exception-driven development [lightning talk]

37
EXCEPTION-DRIVEN DEVELOPMENT ADAM EL-SODANEY AUGUST 2016 @ SYMFONY MEETUP LONDON

Upload: adam-elsodaney

Post on 17-Feb-2017

136 views

Category:

Software


0 download

TRANSCRIPT

EXCEPTION-DRIVEN DEVELOPMENTADAM EL-SODANEY AUGUST 2016 @ SYMFONY MEETUP LONDON

Exception

Throwable

Error

http://php.net/manual/en/class.throwable.php

Exception

LogicException RuntimeException

AmbiguousOptionsException

DivideByZeroException

ExpiredException

BadConfigurationException

RecursiveExceptionCorruptDataException

DerpExceptionOver9000Exception

Exception

LogicException RuntimeException

AmbiguousOptionsException

DivideByZeroException

ExpiredException

BadConfigurationException

RecursiveExceptionCorruptDataException

DerpException Over9000Exception

http://knowyourmeme.com/memes/derp http://knowyourmeme.com/memes/its-over-9000

WHAT IS A LOGIC EXCEPTION ?

php.net/manual/en/class.logicexception.php

[An] Exception that represents [an] error in the program logic.

This kind of exception should lead directly to a fix in your code.

WHAT IS A RUNTIME

EXCEPTION ?

[An] Exception thrown if an error

which can only be found on

runtime occurs.

http://php.net/manual/en/class.runtimeexception.php

WHAT IS A LOGIC EXCEPTION ?

WE

FUCKED

UP!

WHAT IS A RUNTIME

EXCEPTION ?

YOU

FUCKED

UP!

TEXT

TEXT

LET’S CREATE A CALCULATOR

323,013,394,842

divide by

0

LET’S CREATE A BILL CALCULATION APP

€ 323,013,394,842

between

nobody

PREVENT A LOGIC

EXCEPTION …

Cannot divide by zero.

Somebody owes 323 billion Euros

(ie. Greece).

AND MAKE IT A RUNTIME

EXCEPTION .

http://www.bbc.co.uk/news/world-europe-33407742

class MissingInvoiceeException extends \RuntimeException{ }

class DivisionByZeroException extends \LogicException{ }

…WILL BECOME…

class Calculator { public function divide($x, $y) { if (0 === $y) { throw new DivisionByZeroException( 'Cannot divide by zero.’ ); } return $x / $y; } }

LOW-LEVEL API WILL THROW LOGIC EXCEPTION

try { return $this->calculator->divide(100, 0);

} catch (DivisionByZeroException $e) { return INF; }

HIGH-LEVEL API WILL CATCH LOGIC EXCEPTION…

http://math.stackexchange.com/questions/127376/https://en.wikipedia.org/wiki/Division_by_zero#/media/File:Hyperbola_one_over_x.svg

class Till { public function splitBill(Bill $bill, array $customers) { try { $this->calculator->divide($bill->total, count($customers));

} catch (DivisionByZeroException $e) {

throw new MissingInvoiceeException( 'No-one is currently billed for the amount due.’, 5, $e ); }

}}

… OR THROW A RUNTIME EXCEPTION

throw new \Exception( string $message, int $code = 0, \Exception $previous = null );

EXCEPTIONS TAKE 3 ARGUMENTS

http://php.net/manual/en/class.exception.php

ARGUMENT 1

string $message

* Explain why the exception was thrown.

* Never display the message to the end-user.

* This is so they can be translated.

* It may also contain sensitive information.

* Suggest a fix for the exception.

SYMFONY EXCEPTIONS TEND TO MAKE SUGGESTIONS ON HOW TO FIX THEM.

https://github.com/symfony/symfony/blob/2.8/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php#L146

https://github.com/symfony/symfony/blob/2.8/src/Symfony/Component/DependencyInjection/Exception/ScopeCrossingInjectionException.php

OR BETTER YET…

https://twitter.com/DivineOmega/status/696806187526983680

ARGUMENT 3

\Exception $previous

* When one action cannot be performed because another failed.

* When a high-level API causes errors in a low-level API.

* Mistakes occur as the result of a sequence of failures, which can be easily traced.

try { try { try { charge_bill(); } catch (DivideByZeroException $e) { throw new NoInvoiceeException(‘’, 5, $e); } } catch (NoInvoiceeException $f) { if ($debtor = find_debtor()) { chase_debtor($debtor); }

throw new DebtorNotFoundException(‘’, 6, $f); } } catch (DebtorNotFoundException $g) { write_off_debt(); }

A SEQUENCE OF FAILURES CAN BE AUDITED.

DivideByZeroException

NoInvoiceeException

DebtorNotFoundException

charge_bill()

write_off_debt()

ARGUMENT 2

int $code

* Categorize your exceptions…

* …Or identify them.

* Conditions in which the exception occurred can be quickly determined.

* Logging of exceptions is better handled.

ITUNES ERROR CODES

1015

1601

OYSTER CARD ERROR CODES

new BadDataException( ‘Oyster card cannot be read’, $code = 1);

new DoubleEntryException( ‘Card already passed through the gates’, $code = 21);

new InsufficientBalanceException( ‘No valid season ticket or not enough funds.’, $code = 36);

IF OYSTER CARDS WERE MANAGED BY PHP…

https://www.whatdotheyknow.com/request/189010/response/471377/attach/4/Gate%20Reject%20Codes%20FOI%20request.pdf

IATA DELAY CODES

> IATA delay codes were created to standardise the reporting by airlines of commercial flight departure delays.

> Previously, every airline had its own system, which made the sharing and aggregation of flight delay information difficult.

> IATA standardised the flight delay reporting format by using codes that attribute cause and responsibility for the delay.

https://en.wikipedia.org/wiki/IATA_delay_codes

IATA DELAY CODES STARTING WITH…

0 — internal issues

1 — passenger/baggage

2 — cargo/mail

3 — handling

4 — technical

5 — damage/failure

6 — operation

7 — weather

8 — air traffic control

9 — miscellaneous

https://www.eurocontrol.int/sites/default/files/content/documents/official-documents/facts-and-figures/coda-reports/standard-iata-delay-codes-ahm730.pdf

Delay Codes starting with 2 (cargo/mail) These Codes are used to describe delays caused by Cargo (21-26) and Mail Handling (27-29).

21 (CD): Documentation, errors, etc. 22 (CP): Late positioning 23 (CC): Late acceptance 24 (CI): Inadequate packing 25 (CO): Oversales, booking errors 26 (CU): Late preparation in warehouse 27 (CE): Mail Oversales, packing, etc. 28 (CL): Mail Late positioning 29 (CA): Mail Late acceptance

Delay Codes starting with 3 (handling) These Codes are used to describe delays caused by aircraft and ramp handling

31 (GD): Aircraft documentation late or inaccurate, weight and balance (Loadsheet), general declaration, passenger manifest, etc. 32 (GL): Loading, Unloading, bulky/special load, cabin load, lack of loading staff 33 (GE): Loading Equipment, lack of or breakdown, e.g. container pallet loader, lack of staff 34 (GS): Servicing Equipment, lack of or breakdown, lack of staff, e.g. steps 35 (GC): Aircraft Cleaning 36 (GF): Fuelling, Defuelling, fuel supplier 37 (GB): Catering, late delivery or loading 38 (GU): ULD, Containers, pallets, lack of or breakdown 39 (GT): Technical equipment, lack of or breakdown, lack of staff, e.g. pushback

class AircraftCleaningDelayException extends HandlingException { $this->code = 35; }

class HandlingException extends DelayException {}

class DelayException extends \Exception {}

IF IATA DELAYS WERE EXCEPTIONS AND WHERE MANAGED BY PHP…

DOCTRINE EXCEPTIONS “THROW” THEMSELVES

https://github.com/doctrine/doctrine2/blob/2.5/lib/Doctrine/ORM/ORMException.php

Exception

LogicException RuntimeException

OutOfBoundsException

OutOfRangeException

OverflowException

BadFunctionCallException

BadMethodCallException

DomainException

InvalidArgumentException

LengthException RangeException

UnderflowExceptionUnexpectedValueException

http://php.net/manual/en/spl.exceptions.php

LOVE EXCEPTIONS? WHY NOT GO ALL THE WAY…

https://github.com/Enrise/Frisbee

TEXT

FRISBEE IN ACTIONhttps://enrise.com/2016/04/enrise-launches-frisbee/

try { throw new OutOfTimeException( ‘Thanks for listening’ );

} catch (OutOfTimeException $e) {

// Adam Elsodaney

// Senior Symfony developer at REISS

// Follow me on Twitter @ArchFizz }

FIN

https://github.com/adamelso