SlideShare a Scribd company logo
1 of 143
Download to read offline
Hello, From Austin, Texas, USA
https://twitter.com/chrisholland
http://bit.ly/exceptions-ftw
Slides:
Validations
Validations: Painful
Validations: Painful
❖ if (
$this->validateThingOne() &&
$this->validateThingTwo() &&
$this->validateThingThree()
) {
doSomeTransaction();
return true; //signal that the thing was done
} else {
return false; //signal the thing was not done
}
Validations: Painful
❖ private function validateThingOne()
{
if (//someErrorCondition) {
return false;
} else {
return true;
}
}
Validations: Painful
❖ private function validateThingTwo()
{
if (//someErrorCondition) {
return false;
} else {
return true;
}
}
Validations: Painful
❖ private function validateThingThree()
{
if (//someErrorCondition) {
return false;
} else {
return true;
}
}
Validations: Painful
❖ Validation methods must “return true / false” …
❖ to signal success or failure
❖ upstream methods must adapt behavior …
❖ based on true / false
❖ Code has more nesting
Nesting?
Validations: Graceful
Validations: Graceful
❖ $this->validateThingOne();
$this->validateThingTwo();
$this->validateThingThree();
//if any exception thrown above
//doSomeTransaction() will not run
doSomeTransaction();
Validations: Graceful
❖ private function validateThingOne()
{
if (//someErrorCondition) {
throw new InvalidThingOneException();
}
}
Validations: Graceful
❖ private function validateThingTwo()
{
if (//someErrorCondition) {
throw new InvalidThingTwoException();
}
}
Validations: Graceful
❖ private function validateThingThree()
{
if (//someErrorCondition) {
throw new InvalidThingThreeException();
}
}
Validations: Graceful
❖ Validation methods don’t need to return anything
❖ they only need to “throw Exception” on failure
❖ upstream methods don’t have to care
❖ Code has far less nesting.
Another Use-Case
Get Products for User
Get Products for User
❖ Failure Modes:
❖ User has no products …
❖ That’s okay
❖ User could not be found?
❖ That’s not normal
Possible Stack
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
❖ ProductRepository::getProducts(User $user) : array
Possible Stack
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
❖ ProductRepository::getProducts(User $user) : array
Possible Stack
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
❖ ProductRepository::getProducts(User $user) : array
UserRepository::
getUserById(int $userId) : User
getUserById(int $userId) : User
❖ public function getUserById(int $userId)
{
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
}
getUserById(int $userId) : User
❖ public function getUserById(int $userId)
{
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
}
getUserById(int $userId) : User
❖ public function getUserById(int $userId)
{
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
}
What if Bogus?
What Happens When …
… User Not Found?
getUserById(int $userId) : User
❖ What happens when no user is found?
❖ return null?
❖ return false?
❖ return -1?
UserRepository
UserRepository
❖ public function getUserById(int $userId)
{
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
}
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
return null;
}
}
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
return null;
}
}
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
return null; //no Result was found, returning null
}
}
ProductsService
ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
//we don’t want to send a ‘null’ $user to getProducts
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService
❖ If no user is found …
❖ I return an empty array … of Products
❖ … because it feels convenient
❖ is this, however, elegant?
ProductsService
❖ Empty array of Products will be returned if:
❖ correct user, but user has no products
❖ no user was found
ProductsService
❖ Empty array of Products will be returned if:
❖ correct user, but user has no products <— That’s OK
❖ no user was found
ProductsService
❖ Empty array of Products will be returned if:
❖ correct user, but user has no products <— That’s OK
❖ no user was found
ProductsService
❖ Empty array of Products will be returned if:
❖ correct user, but user has no products <— That’s OK
❖ no user was found <— strange behavior
ProductsService: Alternative
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
X
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return -1;
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return -1; //using -1 to “signal” no user found
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return -1; //using -1 to “signal” no user found
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return -1;
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
ProductsService: Alternative
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return -1;
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
Inconsistent Return Types !
ProductsService: Alternative
❖ I return “-1” to “signal” that no user could be found
❖ so my Service can now either return:
❖ an array of products
❖ or -1
❖ … that’s not … “great”.
ProductsController
ProductsController
❖ Based on what the Service returns, the Controller has two
options:
❖ Option 1: also always return an array
❖ Option 2: handle “-1”
ProductsController: Option 1
❖ Option 1: ProductsService always returns an array
❖ ProductsController has no way …
❖ to “detect that no user was found”
❖ ProductsController sends-out empty array in all cases
❖ Consumer of ProductsController has no way to know
that they passed-in a wrong User ID
❖ and goes-on thinking that “user has no products”
ProductsController: Option 2
❖ Option 2: ProductsService returns -1 when no User found
❖ ProductsController must handle that special-case
❖ Send custom error message “User does not exist”
ProductsController: Option 1
ProductsController: Option 1
❖ public function getProducts(int $userId)
{
return $this
->productsService
->getProductsForUser($userId);
}
ProductsController: Option 1
❖ public function getProducts(int $userId)
{
return $this
->productsService
->getProductsForUser($userId)
//if non-existent userId was passed
//empty array is still returned
}
ProductsController: Option 1
❖ public function getProducts(int $userId)
{
return $this
->productsService
->getProductsForUser($userId)
//if non-existent userId was passed
//empty array is still returned
//API consumer is confused :(
}
ProductsController: Option 2
ProductsController: Option 2
❖ public function getProducts(int $userId)
{
return $this
->productsService
->getProductsForUser($userId);
}
ProductsController: Option 2
❖ public function getProducts(int $userId)
{
$products = $this
->productsService
->getProductsForUser($userId);
if ($products === -1) {
$this->sendError(‘Bad User ID: ’.$userId);
} else {
return $products;
}
}
ProductsController: Option 2
❖ public function getProducts(int $userId)
{
$products = $this
->productsService
->getProductsForUser($userId);
if ($products === -1) {
$this->sendError(‘Bad User ID: ’.$userId);
} else {
return $products;
}
}
ProductsController: Option 2
❖ Pros:
❖ We don’t confuse API consumer when they send a bad
User ID
❖ Cons:
❖ More Code
❖ Mixing User-related code with Products-related code
❖ Controller has to know what “-1” means !!!
Impacts
Impacts
❖ UserRepository could be used by dozens of Services
❖ Each Service will have to “check for null” when no User
❖ And in turn, find a way to “Signal” this upstream …
❖ … to the Controllers with “-1”
❖ and Controllers will have to handle “-1”
❖ … every. time.
Impacts
❖ Now …
❖ Multiply the preceding challenges …
❖ By Dozens of Repositories
Impacts
❖ Rampant “Status Codes”: -1, -2, -3, -4
❖ to signal a myriad of error conditions
❖ bound to status messages …
❖ maintained in lookup arrays
Impacts
❖ So. Much. Code.
❖ Corners will be cut.
❖ Strange behavior will creep throughout the system.
Exceptions for the Win
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
return null;
}
}
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
return null;
}
}
X
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
}
}
UserNotFoundException
UserNotFoundException
❖ class UserNotFoundException extends Exception
{
const MESSAGE = ‘User ID could not be found: ’;
public function __construct(string $userId = “”)
{
parent::__construct(
self::MESSAGE.intval($userId),
404,
null
);
}
}
Exceptions Flow
Exceptions Flow
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
Exceptions Flow
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
❖ throws UserNotFoundException
Exceptions Flow
❖ ProductsController::getProducts (int $userId)
❖ ProductsService::getProductsForUser (int $userId) : array
❖ UserRepository::getUserById(int $userId) : User
❖ throws UserNotFoundException
Exceptions Flow
❖ UserNotFoundException will “Bubble-Up”.
❖ No code has to catch it.
❖ Framework can output it in a useful way.
Simplifying Code
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
}
}
Simplifying ProductsService
Simplifying ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
Simplifying ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
if (is_null($user)) {
return [];
}
$products = $this->productsRepo->getProducts($user);
return $products;
}
X
Simplifying ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
$products = $this->productsRepo->getProducts($user);
return $products;
}
Simplifying ProductsService
❖ public function getProductsForUser(int $userId)
{
$user = $this->userRepo->getUserById($id);
return $this->productsRepo->getProducts($user);
}
Simplifying ProductsController
Simplifying ProductsController
❖ public function getProducts(int $userId)
{
$products = $this
->productsService
->getProductsForUser($userId);
if ($products === -1) {
$this->sendError(‘Bad User ID: ’.$userId);
} else {
return $products;
}
}
Simplifying ProductsController
❖ public function getProducts(int $userId)
{
$products = $this
->productsService
->getProductsForUser($userId);
if ($products === -1) {
$this->sendError(‘Bad User ID: ’.$userId);
} else {
return $products;
}
}
X
Simplifying ProductsController
❖ public function getProducts(int $userId)
{
$products = $this
->productsService
->getProductsForUser($userId);
return $products;
}
Simplifying ProductsController
❖ public function getProducts(int $userId)
{
return $this
->productsService
->getProductsForUser($userId);
}
Benefits of Exceptions
Benefits of Exceptions
❖ Less code to handle error cases
❖ Localize signaling at component level
❖ Encapsulate more information
❖ Promote decoupling: upstream components don’t have to
“know” about downstream error cases
Exceptions Best Practices
Exceptions Best Practices
❖ Catch ORM Exceptions and re-throw your own.
❖ Make “lots” of Custom Exception Classes
❖ Think about Exception hierarchy / taxonomy
❖ avoid reusing same exception w/ different error messages
❖ Embed messaging inside exception.
❖ Name them as specifically as possible.
❖ Include helpful messaging as to what happened.
❖ possibly escape or cast inputs - XSS vulnerabilities
❖ intval($userId)
Catching Polymorphic Exceptions
Catching Polymorphic Exceptions
Example: DoctrineORM
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
}
}
UserRepository
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
}
}
ORM::getSingleResult
ORM::getSingleResult
❖ Possible Failure Modes:
❖ No Result was found
❖ More than one Result was found
public function getSingleResult($hydration = null)
{
$result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !==
self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException;
}
if ( ! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
public function getSingleResult($hydration = null)
{
$result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !==
self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException;
}
if ( ! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
public function getSingleResult($hydration = null)
{
$result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !==
self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException;
}
if ( ! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
public function getSingleResult($hydration = null)
{
$result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !==
self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException;
}
if ( ! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
ORM::getSingleResult
❖ getSingleResult can throw either:
❖ NoResultException
❖ NonUniqueResultException
Exception
ORMException
UnexpectedResultException
NonUniqueResultException NoResultException
Polymorphic Exceptions
❖ catch (NoResultException $nre) {
//specific handling of No Result
❖ } catch (UnexpectedResultException $ure) {
//fall-back to any UnexpectedResultException
//other than NoResultException
//which includes NonUniqueResultException
❖ } catch (ORMException $oe) {
//fall-back to any ORMException
//other than UnexpectedResultException
}
Polymorphic Exceptions
❖ catch (NoResultException $nre) {
throw new UserNotFoundException();
❖ } catch (UnexpectedResultException $ure) {
$this->log(‘Unexpected Result: ’.$ure);
❖ } catch (ORMException $oe) {
$this->log(‘ORM Exception: ‘.$oe);
}
Polymorphic Exceptions
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
}
}
Polymorphic Exceptions
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException $e) {
throw new UserNotFoundException($userId);
} catch (UnexpectedResultException $ure) {
$this->log(‘This should not happen: ’.$ure);
throw new UnexpectedApplicationException();
}
}
Catching Exceptions with “|”
Catching Exceptions with “|”
❖ Useful when trying to catch multiple exceptions …
❖ … When no common ancestor is available
Catching Exceptions with “|”
❖ public function getUserById(int $userId)
{
try {
return $this->em->createQuery(
‘select u from E:User u where u.id = :userId’
)
->setParameter(‘userId’, $userId)
->getSingleResult();
} catch (NoResultException | NonUniqueResultException $e) {
throw new UserNotFoundException($userId);
}
}
Exceptions & PHPUnit
Exceptions & PHPUnit
❖ Declare that what comes next will trigger an Exception
❖ Trigger the Exception-throwing behavior
Exceptions & PHPUnit
❖ public function testWrongUserIdThrowsException
{
$bogusUserId = 4815162342;
$this->expectException(UserNotFoundException::class);
$this->userRepo->getUserById($bogusUserId);
}
Exceptions & PHPUnit
❖ public function testWrongUserIdThrowsException
{
$bogusUserId = 4815162342;
$this->expectException(UserNotFoundException::class);
$this->userRepo->getUserById($bogusUserId);
}
Exceptions & PHPUnit
❖ public function testWrongUserIdThrowsException
{
$bogusUserId = 4815162342;
//I am signaling that an exception will be thrown
$this->expectException(UserNotFoundException::class);
$this->userRepo->getUserById($bogusUserId);
}
Exceptions & PHPUnit
❖ public function testWrongUserIdThrowsException
{
$bogusUserId = 4815162342;
//I am signaling that an exception will be thrown
$this->expectException(UserNotFoundException::class);
//I am triggering the exception with $bogusUserId
$this->userRepo->getUserById($bogusUserId);
}
Exceptions & PHPUnit
❖ public function testWrongUserIdThrowsException
{
$bogusUserId = 4815162342;
//I am signaling that an exception will be thrown
$this->expectException(UserNotFoundException::class);
//I am triggering the exception with $bogusUserId
$this->userRepo->getUserById($bogusUserId);
}
TDD Exception Kata
/**
* @throws UserNotFoundException
*/
public function testBogusUserIdThrowsException()
{
$legitimateUser = $this->getRepoNewUser();
/** @var Uuid $bogusUserId */
$bogusUserId = Uuid::uuid4();
self::assertNotNull($legitimateUser->getId());
self::assertFalse($legitimateUser->getId()->equals($bogusUserId));
$this->expectException(UserNotFoundException::class);
$this->userRepository->byId($bogusUserId);
}
public function byId(Uuid $id) : AppUser
{
try {
return $this->em->createQuery(
'select u from E:AppUser u where u.id = :id'
)
->setParameter('id', $id)
->getSingleResult();
} catch (NoResultException | NonUniqueResultException $e) {
throw new UserNotFoundException();
}
}
Interested in TDD?
Interested in TDD?
http://bit.ly/tdd-vids
Takeaways
Fail w/ Poise
Signal Failures w/ Less Code
Handle Failures w/ Less Code
Reduce Technical Debt
Build Robust Systems
Exceptions are your Friend
Thank You!
https://twitter.com/chrisholland
Slides:
http://bit.ly/exceptions-ftw

More Related Content

What's hot

Javascript & jQuery: A pragmatic introduction
Javascript & jQuery: A pragmatic introductionJavascript & jQuery: A pragmatic introduction
Javascript & jQuery: A pragmatic introductionIban Martinez
 
Strategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux ApplicaitonsStrategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux Applicaitonsgarbles
 
Coding for Scale and Sanity
Coding for Scale and SanityCoding for Scale and Sanity
Coding for Scale and SanityJimKellerES
 
Data20161007
Data20161007Data20161007
Data20161007capegmail
 
jQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journeyjQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journeyHuiyi Yan
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConRafael Dohms
 
Idioms in swift 2016 05c
Idioms in swift 2016 05cIdioms in swift 2016 05c
Idioms in swift 2016 05cKaz Yoshikawa
 
Sins Against Drupal 2
Sins Against Drupal 2Sins Against Drupal 2
Sins Against Drupal 2Aaron Crosman
 
Continuous Integration: a practical approach
Continuous Integration: a practical approachContinuous Integration: a practical approach
Continuous Integration: a practical approachESUG
 
Command-Oriented Architecture
Command-Oriented ArchitectureCommand-Oriented Architecture
Command-Oriented ArchitectureLuiz Messias
 
How to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI ComponentsHow to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI Componentscagataycivici
 
Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testingsmontanari
 
Deep-dive into Django #1
Deep-dive into Django #1Deep-dive into Django #1
Deep-dive into Django #1Avik Das
 
React.js workshop by tech47.in
React.js workshop by tech47.inReact.js workshop by tech47.in
React.js workshop by tech47.inJaikant Kumaran
 
Imagine a world without mocks
Imagine a world without mocksImagine a world without mocks
Imagine a world without mockskenbot
 
WordPress Capabilities Magic
WordPress Capabilities MagicWordPress Capabilities Magic
WordPress Capabilities Magicmannieschumpert
 

What's hot (20)

Unit testing UIView
Unit testing UIViewUnit testing UIView
Unit testing UIView
 
Javascript & jQuery: A pragmatic introduction
Javascript & jQuery: A pragmatic introductionJavascript & jQuery: A pragmatic introduction
Javascript & jQuery: A pragmatic introduction
 
Rspec and Rails
Rspec and RailsRspec and Rails
Rspec and Rails
 
Strategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux ApplicaitonsStrategies for Mitigating Complexity in React Based Redux Applicaitons
Strategies for Mitigating Complexity in React Based Redux Applicaitons
 
Coding for Scale and Sanity
Coding for Scale and SanityCoding for Scale and Sanity
Coding for Scale and Sanity
 
Data20161007
Data20161007Data20161007
Data20161007
 
jQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journeyjQuery Data Manipulate API - A source code dissecting journey
jQuery Data Manipulate API - A source code dissecting journey
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnCon
 
Idioms in swift 2016 05c
Idioms in swift 2016 05cIdioms in swift 2016 05c
Idioms in swift 2016 05c
 
Sins Against Drupal 2
Sins Against Drupal 2Sins Against Drupal 2
Sins Against Drupal 2
 
Continuous Integration: a practical approach
Continuous Integration: a practical approachContinuous Integration: a practical approach
Continuous Integration: a practical approach
 
Command-Oriented Architecture
Command-Oriented ArchitectureCommand-Oriented Architecture
Command-Oriented Architecture
 
How to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI ComponentsHow to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI Components
 
Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testing
 
Deep-dive into Django #1
Deep-dive into Django #1Deep-dive into Django #1
Deep-dive into Django #1
 
jQuery Presentasion
jQuery PresentasionjQuery Presentasion
jQuery Presentasion
 
React.js workshop by tech47.in
React.js workshop by tech47.inReact.js workshop by tech47.in
React.js workshop by tech47.in
 
Imagine a world without mocks
Imagine a world without mocksImagine a world without mocks
Imagine a world without mocks
 
Handlebars.js
Handlebars.jsHandlebars.js
Handlebars.js
 
WordPress Capabilities Magic
WordPress Capabilities MagicWordPress Capabilities Magic
WordPress Capabilities Magic
 

Similar to Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"

Guard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful SecurityGuard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful SecurityRyan Weaver
 
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreRyan Weaver
 
PHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにPHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにYuya Takeyama
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
Introduction to DI(C)
Introduction to DI(C)Introduction to DI(C)
Introduction to DI(C)Radek Benkel
 
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdfJAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdfcalderoncasto9163
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
How kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonHow kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonKris Wallsmith
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mockingKonstantin Kudryashov
 
Introduction to Software Testing
Introduction to Software TestingIntroduction to Software Testing
Introduction to Software TestingSergio Arroyo
 
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...DevClub_lv
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitmfrost503
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitmfrost503
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
Web::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTPWeb::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTPMichael Francis
 
Serial(ize) killers, czyli jak popsuliśmy API
Serial(ize) killers, czyli jak popsuliśmy APISerial(ize) killers, czyli jak popsuliśmy API
Serial(ize) killers, czyli jak popsuliśmy APIPiotr Horzycki
 

Similar to Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling" (20)

Guard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful SecurityGuard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful Security
 
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
 
PHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにPHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くために
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Introduction to DI(C)
Introduction to DI(C)Introduction to DI(C)
Introduction to DI(C)
 
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdfJAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
SOLID Principles
SOLID PrinciplesSOLID Principles
SOLID Principles
 
How kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonHow kris-writes-symfony-apps-london
How kris-writes-symfony-apps-london
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Introduction to Software Testing
Introduction to Software TestingIntroduction to Software Testing
Introduction to Software Testing
 
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
 
PHP Unit Testing
PHP Unit TestingPHP Unit Testing
PHP Unit Testing
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
 
Taming Command Bus
Taming Command BusTaming Command Bus
Taming Command Bus
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
Web::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTPWeb::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTP
 
Serial(ize) killers, czyli jak popsuliśmy API
Serial(ize) killers, czyli jak popsuliśmy APISerial(ize) killers, czyli jak popsuliśmy API
Serial(ize) killers, czyli jak popsuliśmy API
 
BDD de fuera a dentro
BDD de fuera a dentroBDD de fuera a dentro
BDD de fuera a dentro
 

More from Fwdays

"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y..."How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...Fwdays
 
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil TopchiiFwdays
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro Spodarets"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro SpodaretsFwdays
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua", Maksym KindritskyiFwdays
 
"Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl..."Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl...Fwdays
 
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T..."How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...Fwdays
 
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ..."The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...Fwdays
 
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu..."[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...Fwdays
 
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care..."[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...Fwdays
 
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"..."4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...Fwdays
 
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast..."Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...Fwdays
 
"Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others..."Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others...Fwdays
 
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?", Oleksandra MyronovaFwdays
 
"Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv..."Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv...Fwdays
 
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin..."How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...Fwdays
 

More from Fwdays (20)

"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y..."How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
 
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro Spodarets"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro Spodarets
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
 
"Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl..."Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl...
 
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T..."How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
 
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ..."The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
 
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu..."[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
 
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care..."[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
 
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"..."4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
 
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast..."Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
 
"Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others..."Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others...
 
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
 
"Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv..."Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv...
 
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin..."How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
 

Recently uploaded

Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clashcharlottematthew16
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesZilliz
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
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
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piececharlottematthew16
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embeddingZilliz
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 

Recently uploaded (20)

Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clash
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector Databases
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
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
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piece
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embedding
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 

Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"

  • 1.
  • 2. Hello, From Austin, Texas, USA
  • 3.
  • 8. Validations: Painful ❖ if ( $this->validateThingOne() && $this->validateThingTwo() && $this->validateThingThree() ) { doSomeTransaction(); return true; //signal that the thing was done } else { return false; //signal the thing was not done }
  • 9. Validations: Painful ❖ private function validateThingOne() { if (//someErrorCondition) { return false; } else { return true; } }
  • 10. Validations: Painful ❖ private function validateThingTwo() { if (//someErrorCondition) { return false; } else { return true; } }
  • 11. Validations: Painful ❖ private function validateThingThree() { if (//someErrorCondition) { return false; } else { return true; } }
  • 12. Validations: Painful ❖ Validation methods must “return true / false” … ❖ to signal success or failure ❖ upstream methods must adapt behavior … ❖ based on true / false ❖ Code has more nesting
  • 14.
  • 16. Validations: Graceful ❖ $this->validateThingOne(); $this->validateThingTwo(); $this->validateThingThree(); //if any exception thrown above //doSomeTransaction() will not run doSomeTransaction();
  • 17. Validations: Graceful ❖ private function validateThingOne() { if (//someErrorCondition) { throw new InvalidThingOneException(); } }
  • 18. Validations: Graceful ❖ private function validateThingTwo() { if (//someErrorCondition) { throw new InvalidThingTwoException(); } }
  • 19. Validations: Graceful ❖ private function validateThingThree() { if (//someErrorCondition) { throw new InvalidThingThreeException(); } }
  • 20. Validations: Graceful ❖ Validation methods don’t need to return anything ❖ they only need to “throw Exception” on failure ❖ upstream methods don’t have to care ❖ Code has far less nesting.
  • 23. Get Products for User ❖ Failure Modes: ❖ User has no products … ❖ That’s okay ❖ User could not be found? ❖ That’s not normal
  • 24. Possible Stack ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User ❖ ProductRepository::getProducts(User $user) : array
  • 25. Possible Stack ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User ❖ ProductRepository::getProducts(User $user) : array
  • 26. Possible Stack ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User ❖ ProductRepository::getProducts(User $user) : array
  • 28. getUserById(int $userId) : User ❖ public function getUserById(int $userId) { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); }
  • 29. getUserById(int $userId) : User ❖ public function getUserById(int $userId) { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); }
  • 30. getUserById(int $userId) : User ❖ public function getUserById(int $userId) { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } What if Bogus?
  • 31. What Happens When … … User Not Found?
  • 32.
  • 33. getUserById(int $userId) : User ❖ What happens when no user is found? ❖ return null? ❖ return false? ❖ return -1?
  • 35. UserRepository ❖ public function getUserById(int $userId) { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); }
  • 36. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { return null; } }
  • 37. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { return null; } }
  • 38. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { return null; //no Result was found, returning null } }
  • 40. ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 41. ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 42. ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } //we don’t want to send a ‘null’ $user to getProducts $products = $this->productsRepo->getProducts($user); return $products; }
  • 43. ProductsService ❖ If no user is found … ❖ I return an empty array … of Products ❖ … because it feels convenient ❖ is this, however, elegant?
  • 44. ProductsService ❖ Empty array of Products will be returned if: ❖ correct user, but user has no products ❖ no user was found
  • 45. ProductsService ❖ Empty array of Products will be returned if: ❖ correct user, but user has no products <— That’s OK ❖ no user was found
  • 46. ProductsService ❖ Empty array of Products will be returned if: ❖ correct user, but user has no products <— That’s OK ❖ no user was found
  • 47. ProductsService ❖ Empty array of Products will be returned if: ❖ correct user, but user has no products <— That’s OK ❖ no user was found <— strange behavior
  • 49. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 50. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; } X
  • 51. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return -1; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 52. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return -1; //using -1 to “signal” no user found } $products = $this->productsRepo->getProducts($user); return $products; }
  • 53. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return -1; //using -1 to “signal” no user found } $products = $this->productsRepo->getProducts($user); return $products; }
  • 54. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return -1; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 55. ProductsService: Alternative ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return -1; } $products = $this->productsRepo->getProducts($user); return $products; } Inconsistent Return Types !
  • 56. ProductsService: Alternative ❖ I return “-1” to “signal” that no user could be found ❖ so my Service can now either return: ❖ an array of products ❖ or -1 ❖ … that’s not … “great”.
  • 58. ProductsController ❖ Based on what the Service returns, the Controller has two options: ❖ Option 1: also always return an array ❖ Option 2: handle “-1”
  • 59. ProductsController: Option 1 ❖ Option 1: ProductsService always returns an array ❖ ProductsController has no way … ❖ to “detect that no user was found” ❖ ProductsController sends-out empty array in all cases ❖ Consumer of ProductsController has no way to know that they passed-in a wrong User ID ❖ and goes-on thinking that “user has no products”
  • 60. ProductsController: Option 2 ❖ Option 2: ProductsService returns -1 when no User found ❖ ProductsController must handle that special-case ❖ Send custom error message “User does not exist”
  • 62. ProductsController: Option 1 ❖ public function getProducts(int $userId) { return $this ->productsService ->getProductsForUser($userId); }
  • 63. ProductsController: Option 1 ❖ public function getProducts(int $userId) { return $this ->productsService ->getProductsForUser($userId) //if non-existent userId was passed //empty array is still returned }
  • 64. ProductsController: Option 1 ❖ public function getProducts(int $userId) { return $this ->productsService ->getProductsForUser($userId) //if non-existent userId was passed //empty array is still returned //API consumer is confused :( }
  • 66. ProductsController: Option 2 ❖ public function getProducts(int $userId) { return $this ->productsService ->getProductsForUser($userId); }
  • 67. ProductsController: Option 2 ❖ public function getProducts(int $userId) { $products = $this ->productsService ->getProductsForUser($userId); if ($products === -1) { $this->sendError(‘Bad User ID: ’.$userId); } else { return $products; } }
  • 68. ProductsController: Option 2 ❖ public function getProducts(int $userId) { $products = $this ->productsService ->getProductsForUser($userId); if ($products === -1) { $this->sendError(‘Bad User ID: ’.$userId); } else { return $products; } }
  • 69. ProductsController: Option 2 ❖ Pros: ❖ We don’t confuse API consumer when they send a bad User ID ❖ Cons: ❖ More Code ❖ Mixing User-related code with Products-related code ❖ Controller has to know what “-1” means !!!
  • 71. Impacts ❖ UserRepository could be used by dozens of Services ❖ Each Service will have to “check for null” when no User ❖ And in turn, find a way to “Signal” this upstream … ❖ … to the Controllers with “-1” ❖ and Controllers will have to handle “-1” ❖ … every. time.
  • 72. Impacts ❖ Now … ❖ Multiply the preceding challenges … ❖ By Dozens of Repositories
  • 73.
  • 74. Impacts ❖ Rampant “Status Codes”: -1, -2, -3, -4 ❖ to signal a myriad of error conditions ❖ bound to status messages … ❖ maintained in lookup arrays
  • 75. Impacts ❖ So. Much. Code. ❖ Corners will be cut. ❖ Strange behavior will creep throughout the system.
  • 77.
  • 78. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { return null; } }
  • 79. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { return null; } } X
  • 80. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } }
  • 82. UserNotFoundException ❖ class UserNotFoundException extends Exception { const MESSAGE = ‘User ID could not be found: ’; public function __construct(string $userId = “”) { parent::__construct( self::MESSAGE.intval($userId), 404, null ); } }
  • 84. Exceptions Flow ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User
  • 85. Exceptions Flow ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User ❖ throws UserNotFoundException
  • 86. Exceptions Flow ❖ ProductsController::getProducts (int $userId) ❖ ProductsService::getProductsForUser (int $userId) : array ❖ UserRepository::getUserById(int $userId) : User ❖ throws UserNotFoundException
  • 87. Exceptions Flow ❖ UserNotFoundException will “Bubble-Up”. ❖ No code has to catch it. ❖ Framework can output it in a useful way.
  • 89. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } }
  • 91. Simplifying ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; }
  • 92. Simplifying ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); if (is_null($user)) { return []; } $products = $this->productsRepo->getProducts($user); return $products; } X
  • 93. Simplifying ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); $products = $this->productsRepo->getProducts($user); return $products; }
  • 94. Simplifying ProductsService ❖ public function getProductsForUser(int $userId) { $user = $this->userRepo->getUserById($id); return $this->productsRepo->getProducts($user); }
  • 96. Simplifying ProductsController ❖ public function getProducts(int $userId) { $products = $this ->productsService ->getProductsForUser($userId); if ($products === -1) { $this->sendError(‘Bad User ID: ’.$userId); } else { return $products; } }
  • 97. Simplifying ProductsController ❖ public function getProducts(int $userId) { $products = $this ->productsService ->getProductsForUser($userId); if ($products === -1) { $this->sendError(‘Bad User ID: ’.$userId); } else { return $products; } } X
  • 98. Simplifying ProductsController ❖ public function getProducts(int $userId) { $products = $this ->productsService ->getProductsForUser($userId); return $products; }
  • 99. Simplifying ProductsController ❖ public function getProducts(int $userId) { return $this ->productsService ->getProductsForUser($userId); }
  • 100.
  • 102. Benefits of Exceptions ❖ Less code to handle error cases ❖ Localize signaling at component level ❖ Encapsulate more information ❖ Promote decoupling: upstream components don’t have to “know” about downstream error cases
  • 104. Exceptions Best Practices ❖ Catch ORM Exceptions and re-throw your own. ❖ Make “lots” of Custom Exception Classes ❖ Think about Exception hierarchy / taxonomy ❖ avoid reusing same exception w/ different error messages ❖ Embed messaging inside exception. ❖ Name them as specifically as possible. ❖ Include helpful messaging as to what happened. ❖ possibly escape or cast inputs - XSS vulnerabilities ❖ intval($userId)
  • 107. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } }
  • 108. UserRepository ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } }
  • 110. ORM::getSingleResult ❖ Possible Failure Modes: ❖ No Result was found ❖ More than one Result was found
  • 111. public function getSingleResult($hydration = null) { $result = $this->execute(null, $hydrationMode); if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { throw new NoResultException; } if ( ! is_array($result)) { return $result; } if (count($result) > 1) { throw new NonUniqueResultException; } return array_shift($result); }
  • 112. public function getSingleResult($hydration = null) { $result = $this->execute(null, $hydrationMode); if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { throw new NoResultException; } if ( ! is_array($result)) { return $result; } if (count($result) > 1) { throw new NonUniqueResultException; } return array_shift($result); }
  • 113. public function getSingleResult($hydration = null) { $result = $this->execute(null, $hydrationMode); if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { throw new NoResultException; } if ( ! is_array($result)) { return $result; } if (count($result) > 1) { throw new NonUniqueResultException; } return array_shift($result); }
  • 114. public function getSingleResult($hydration = null) { $result = $this->execute(null, $hydrationMode); if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { throw new NoResultException; } if ( ! is_array($result)) { return $result; } if (count($result) > 1) { throw new NonUniqueResultException; } return array_shift($result); }
  • 115. ORM::getSingleResult ❖ getSingleResult can throw either: ❖ NoResultException ❖ NonUniqueResultException
  • 117. Polymorphic Exceptions ❖ catch (NoResultException $nre) { //specific handling of No Result ❖ } catch (UnexpectedResultException $ure) { //fall-back to any UnexpectedResultException //other than NoResultException //which includes NonUniqueResultException ❖ } catch (ORMException $oe) { //fall-back to any ORMException //other than UnexpectedResultException }
  • 118. Polymorphic Exceptions ❖ catch (NoResultException $nre) { throw new UserNotFoundException(); ❖ } catch (UnexpectedResultException $ure) { $this->log(‘Unexpected Result: ’.$ure); ❖ } catch (ORMException $oe) { $this->log(‘ORM Exception: ‘.$oe); }
  • 119. Polymorphic Exceptions ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } }
  • 120. Polymorphic Exceptions ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException $e) { throw new UserNotFoundException($userId); } catch (UnexpectedResultException $ure) { $this->log(‘This should not happen: ’.$ure); throw new UnexpectedApplicationException(); } }
  • 122. Catching Exceptions with “|” ❖ Useful when trying to catch multiple exceptions … ❖ … When no common ancestor is available
  • 123. Catching Exceptions with “|” ❖ public function getUserById(int $userId) { try { return $this->em->createQuery( ‘select u from E:User u where u.id = :userId’ ) ->setParameter(‘userId’, $userId) ->getSingleResult(); } catch (NoResultException | NonUniqueResultException $e) { throw new UserNotFoundException($userId); } }
  • 125. Exceptions & PHPUnit ❖ Declare that what comes next will trigger an Exception ❖ Trigger the Exception-throwing behavior
  • 126. Exceptions & PHPUnit ❖ public function testWrongUserIdThrowsException { $bogusUserId = 4815162342; $this->expectException(UserNotFoundException::class); $this->userRepo->getUserById($bogusUserId); }
  • 127. Exceptions & PHPUnit ❖ public function testWrongUserIdThrowsException { $bogusUserId = 4815162342; $this->expectException(UserNotFoundException::class); $this->userRepo->getUserById($bogusUserId); }
  • 128. Exceptions & PHPUnit ❖ public function testWrongUserIdThrowsException { $bogusUserId = 4815162342; //I am signaling that an exception will be thrown $this->expectException(UserNotFoundException::class); $this->userRepo->getUserById($bogusUserId); }
  • 129. Exceptions & PHPUnit ❖ public function testWrongUserIdThrowsException { $bogusUserId = 4815162342; //I am signaling that an exception will be thrown $this->expectException(UserNotFoundException::class); //I am triggering the exception with $bogusUserId $this->userRepo->getUserById($bogusUserId); }
  • 130. Exceptions & PHPUnit ❖ public function testWrongUserIdThrowsException { $bogusUserId = 4815162342; //I am signaling that an exception will be thrown $this->expectException(UserNotFoundException::class); //I am triggering the exception with $bogusUserId $this->userRepo->getUserById($bogusUserId); }
  • 132. /** * @throws UserNotFoundException */ public function testBogusUserIdThrowsException() { $legitimateUser = $this->getRepoNewUser(); /** @var Uuid $bogusUserId */ $bogusUserId = Uuid::uuid4(); self::assertNotNull($legitimateUser->getId()); self::assertFalse($legitimateUser->getId()->equals($bogusUserId)); $this->expectException(UserNotFoundException::class); $this->userRepository->byId($bogusUserId); }
  • 133. public function byId(Uuid $id) : AppUser { try { return $this->em->createQuery( 'select u from E:AppUser u where u.id = :id' ) ->setParameter('id', $id) ->getSingleResult(); } catch (NoResultException | NonUniqueResultException $e) { throw new UserNotFoundException(); } }
  • 138. Signal Failures w/ Less Code
  • 139. Handle Failures w/ Less Code