Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Guided by Examples

Guided by Examples

One of the most challenging problems of developing business-oriented software is mis-communication. A disconnect between the business itself and the actual developers. Misunderstandings can be costly. Delivering the wrong thing following months of development can be devastating.

How do we make sure we are building precisely what the business requires? How can we avoid wasting time building more than is needed (a.k.a gold plating). And finally, How can working from real examples of how a system should work can drive code that fulfils the real needs of the client. We need a more "business-aware" form of coding.

Following BDD principles, starting from high-level scenarios, that broadly drive the application, down to specifications that drive the code, I will show how we can write software guided by examples; Examples which are derived from the very requirements of the business.

Avatar for Jon Acker

Jon Acker

June 08, 2018

More Decks by Jon Acker

Other Decks in Programming

Transcript

  1. Yeah, so... I want a mobile app with a big

    green button that the customer presses and a courier appears on their doorstep to pick up a parcel, then the courier delivers it to another address. If he’s not free the next courier in line should be notified. No, no, if there’s a one closer to them he should be dispatched first, there’s no point dispatching someone from 10 miles away if there’s one in the next street is there? No, I don’t care how they’re notified. What??? How should I know what the heck a queuing system is... Whatever... Just notify the first one who’s available. God – why is this so
  2. USER STORIES ??? Booking a delivery As a User In

    order to book a delivery I want to be able to use the DeliverTo app to book Given the is a courier available When I press the button to book a delivery Then the delivery should be booked
  3. Meanwhile - the dev team are struggling… There’s definitely a

    booking button and when you press it a courier arrives Are you sure we need to do this in react??
  4. “… a tremendous amount of time & money wasted due

    to wrong assumptions […] poor communication of objectives, lack of understanding and misalignment with overall goals ” - Gojko Adzik 2009
  5. Scale deliveries by 500% IT Operations Marketing Couriers Email Campaign

    Engage Customers Centralised Scheduling System Customer Booking App Automate the booking system! Courier Mobile App
  6. Scale deliveries by 500% IT Operations Centralised Scheduling System Automate

    the booking system! The Fog of Ambiguity and Despair Book
  7. “BDD is the art of using examples in conversation to

    illustrate behaviour” Liz Keogh, 2014
  8. For Example? Customer books a delivery for a specific time.

    DeliverTo checks to see who is available for that time and schedules that courier to pick up a parcel from that customer at the time specified by the customer. The customer then gets a confirmation that the courier will arrive at the pickup time of their choice.
  9. A Concrete Example James books a delivery from Den Haag

    to Utrecht for 13:00 Nick, who works as a courier for DeliverTo, was scheduled earlier to deliver to an address in Utrecht by 13:00 Therefore Nick will be available and gets scheduled to pick up from Den Haag at 13:00, and James will immediately receive a pickup confirmation.
  10. An Even More Concrete Example James books a delivery from

    DenHaag to Amsterdam for 13:00 . Nick works as a courier with DeliverTo, his average speed is 20 kmh. Nick was scheduled earlier to pickup from Amsterdam at 12:30 (which is is 10 km from Den Haag) delivering Den Haag Therefore Nick will be available and gets scheduled to pick up from Den Haag at 13:00, and James will receive a pickup confirmation.
  11. Booking a delivery If no couriers available - customer told:

    cannot book right now Nick was scheduled to deliver from Amsterdam to Utrecht for 14:00 James books a delivery, picking up from Den Haag at 14:15 => Customer told booking cannot be taken Couriers speed is 20kph Nick is a courier registered with DeliverTo James is a DeliverTo customer Amsterdam is 10km from Utrecht. Utrecht is 20km from Den Haag Nick was scheduled to deliver from Amsterdam to Utrecht for 14:00 James books a delivery, picking up from Den Haag. at 15:00 => Customer told booking cannot be taken
  12. If no couriers available - customer told: cannot book right

    now, otherwise they are scheduled and customer notified Nick was scheduled to deliver from Amsterdam to Utrecht for 14:00 James books a delivery, picking up from Den Haag. at 15:00 => Customer told booking cannot be taken Nick was scheduled to deliver from Amsterdam to Utrecht for 14:00 James books a delivery, from Den Haag at 15:00 => Customer told booking cannot be taken Nick was scheduled to deliver from Amsterdam to Utrecht for 13:30 James books a delivery, picking up from Den Haag at 15:00 => Nick scheduled to pickup from Den Haag at 15:00 And James has picked confirmed for 15:00 Utrecht is 20km from Den Haag Amsterdam t is 10km from Utrecht. Nick is a courier James is a customer Couriers speed 20kph
  13. Nick was scheduled to deliver from Amsterdam to Utrecht for

    14:00 James books a delivery, picking up from Den Haag. at 15:00 => Customer told booking cannot be taken If no couriers available - customer told: cannot book right now, otherwise they are scheduled and customer notified If both couriers available - the closest one to the customer gets scheduled Nick was scheduled to deliver from Amsterdam to Utrecht for 13:30 Mark was scheduled to deliver from Utrech to Den Haag for 13:30 James books a delivery, picking up from Den Haag at 15:00 going to Eindhoven ==> Mark scheduled to pickup from Den Haag at 15:00 And James has picked confirmed for 15:00 Nick was scheduled to deliver from Amsterdam to Utrecht for 14:00 James books a delivery, from Den Haag at 15:00 ==> Customer told booking cannot be taken Nick was scheduled to deliver from Amsterdam to Utrecht for 13:30 James books a delivery, picking up from Den Haag at 15:00 ==> Nick scheduled to pickup from Den Haag at 15:00 And James has picked confirmed for 15:00 Utrecht is 20km from Den Haag Amsterdam t is 10km from Utrecht. Nick is a courier James is a customer Couriers speed 20kph
  14. GIVEN SOMETHING HAPPENED IN THE PAST WHEN THIS COMMAND IS

    GIVEN THEN OUTCOME SHOULD HAVE HAPPENED
  15. ARRANGE PUT SYSTEM IN A GIVEN STATE ACT TAKE THE

    ACTION TO TEST ASSERT RESULTING STATE SHOULD BE CORRECT
  16. Background: Given the distance from "Amsterdam" to "Utrecht" is 20

    km And a couriers average speed is 20kph Scenario: Not scheduling courier when they are busy Given Nick was scheduled to deliver from Amsterdam to Utrech for 12:30 When James tries to book a delivery from Utrech to Den Haag for 13:00 Then Nick should not have been scheduled to pickup from Utrecht 13:00 And James should be told that no couriers are available
  17. Background: Given the distance from "Amsterdam" to "Utrecht" is 20

    km And a couriers average speed is 20kph Scenario: Not scheduling courier when they are busy Given Nick was scheduled to deliver from Amsterdam to Utrech for 12:30 When James tries to book a delivery from Utrech to Den Haag for 13:00 Then Nick should not have been scheduled to pickup from Utrecht 13:00 And James should be told that no couriers are available
  18. Background: Given the distance from "Amsterdam" to “Den Haag" is

    20 km And a couriers average speed is 20kph Scenario: Not scheduling courier when they are busy Given Nick was scheduled to deliver from Amsterdam to Den Haag for 12:00 When James tries to book a delivery from Den Haag to Utrech for 13:00 Then Nick should have been scheduled to pickup from Den Haag at 13:00 And James should have received a confirmation of their booking Set up a stubbed response for a request for this distance Hard code for now? Create a schedule for nick with these destinations and pickup time Make the actual request to book the delivery from this pickup address Assert that Nick was not actually scheduled and that James is told why.
  19. public function __construct() { $this->map = new InMemoryMap(); $this->schedule =

    new InMemorySchedule(); $this->messageGateway = new InMemoryMessageGateway(); $this->system = new DeliverTo\System( $this->schedule, $this->map, $this->messageGateway ); } /** * @Given the distance from :address1 to :address2 is :distance kilometers */ public function theDistanceFromToIsKilometers($address1, address2 ...) { $this->map->setDistance($address1, $address2, $distance); }
  20. /** * @Given :name is registered as a courier with

    DeliverTo */ public function nickIsRegisteredAsACourierWithDeliverto(string $name) { $this->system->registerCourier(Courier::named($name)); } /** * @Given :name is a customer of DeliverTo */ public function jamesIsACustomerOfDeliverto(string $name) { $this->system->registerCustomer(Customer::named($name)); }
  21. /** * @Given :nick was scheduled to deliver from :pickupAddress

    to :dropoffAddress for :hours::minutes */ public function nickWasScheduledToDeliverFromToFor( Courier $nick, string $pickupAddress, string $dropoffAddress, int $hours, int $mins { $time = new DateTime("$hours:$mins:00"); $this->schedule->add($nick, Delivery::from($pickupAddress) ->to($dropoffAddress) ->for($time) ); }
  22. /** * @When :customer tries to book a delivery from

    :pickupAddress to :dropoffAddress for :hours::minutes */ public function jamesTriesToBookADeliveryFromToFor( Customer $customer, string $pickupAddress ...) { $this->delivery = Delivery::from($pickupAddress) ->to($dropoffAddress) ->for(new DateTime("$hours:$minutes:00")); try { $this->system->book($this->delivery, $customer); } catch (RuntimeException $e) { $this->caughtException = $e; } }
  23. /** * @Then this delivery should have been scheduled with

    :nick */ public function thisDeliveryShouldHaveBeenScheduleWith(Courier $nick) { Assert::assertContains( $this->delivery, $this->schedule->getDeliveriesFor($nick) ); }
  24. /** * @Then :james should received a confirmation of his

    booking */ public function jamesShouldReceivedAConfirmationOfHisBooking(Customer $james) { Assert::assertTrue( $this->messageGateway->messageWasSent(Message::to($james)), 'Expected Customer to receive confirmation!’ ); }
  25. /** * @param Delivery $requestedDelivery * @param Customer $customer */

    public function book(Delivery $requestedDelivery, Customer $customer) { } There’s nothing in here yet, and to make our example pass, we need something to happen here.
  26. /** * @param Delivery $requestedDelivery * @param Customer $customer */

    public function book(Delivery $requestedDelivery, Customer $customer) { $this->schedule->add($this->couriers[0], $requestedDelivery); }
  27. /** * @param Delivery $requestedDelivery * @param Customer $customer */

    public function book(Delivery $requestedDelivery, Customer $customer) { $this->schedule->add($this->couriers[0], $requestedDelivery); $this->messageGateway->send(Message::to($customer)); }
  28. public function book(Delivery $requestedDelivery, Customer $customer) { $lastDelivery = $this->schedule->getLastDeliveryFor($this->couriers[0]);

    $lastDropoffTime = $lastDelivery->calculateTransitTimeUsing($this->map); $estimatedPickupTime = $delivery->getPickupTime()->add( new DateInterval('PT'.$lastDropoffTime.'M') ); if ($estimatedPickupTime <= $deliveryRequest->getPickupTime()) { $this->schedule->add($this->couriers[0], $requestedDelivery); $this->messageGateway->send(Message::to($customer)); return; } throw new \RuntimeException(); }
  29. public function book(Delivery $requestedDelivery, Customer $customer) { $lastDelivery = $this->schedule->getLastDeliveryFor($this->couriers[0]);

    $estimatedPickupTime = $lastDelivery->calculateDropoffTimeUsing($this->map); if ($estimatedPickupTime <= $requestedDelivery->getPickupTime()) { $this->schedule->add($this->couriers[0], $requestedDelivery); $this->messageGateway->send(Message::to($customer)); return; } throw new RuntimeException(); }
  30. public function book(Delivery $requestedDelivery, Customer $customer) { $lastDelivery = $this->schedule->getLastDeliveryFor($this->couriers[0]);

    $estimatedPickupTime = $lastDelivery ->calculateDropoffTimeUsing($this->map); $estimatedPickupTime->add($lastDelivery ->calculateTransitTimeTo($requestedDelivery) ->using($this->map) ); if ($requestedDelivery->hasPickupBefore($estimatedPickupTime)) { $this->schedule->add($this->couriers[0], $requestedDelivery); $this->messageGateway->send(Message::to($customer)); return; } throw new RuntimeException(); } Add transit time
  31. public function book(Delivery $requestedDelivery, Customer $customer) { $lastDelivery = $this->schedule->getLastDeliveryFor($this->couriers[0]);

    $estimatedPickupTime = $lastDelivery ->calculateDropoffTimeUsing($this->map) ->add($lastDelivery ->calculateTransitTimeTo($requestedDelivery) ->using($this->map) ); if ($requestedDelivery->hasPickupBefore($estimatedPickupTime)) { $this->schedule->add($this->couriers[0], $requestedDelivery); $this->messageGateway->send(Message::to($customer)); return; } throw new RuntimeException(); }
  32. Run the scenarios in the “domain” suite - no controllers,

    no database Run the scenarios in the “e2e” suite - use controllers, routing and a real database
  33. /** * @var DeliverTo\System */ private $deliverTo; public function __construct(System

    $deliverTo) { $this->deliverTo = $deliverTo; } /** * @Route("/book") */ public function bookAction(Customer $customer, string $pickupAddress, ...) { $delivery = Delivery::from($pickupAddress) ->to($dropoffAddress) ->for(new DateTime("$hours:$minutes:00")); $this->deliverTo->book($delivery, $customer); }
  34. services: DeliverTo\System: arguments: - '@google.map' - '@delivery_to.schedule' - '@deliver_to.message_gateway' delivery_to.schedule:

    class: InMemorySchedule deliver_to.message_gateway: class: InMemoryMessageGateway google.map: class: InFileMap
  35. Behat / Cucumber - via Extensions Serenity BDD - Built

    In Concordion - Executable Markdown
  36. Recommended reading: Specification by Example - Gojko Adzik BDD In

    Action - John Ferguson Smart Behaviour Driven Development - Liz Keogh (leanpub) The Cucumber Book - Aslek Hellesoy, Sen Rose, Matt Wynne Discovery: Explore Behaviour Using Examples - Gaspar Nagy & Seb Rose