$30 off During Our Annual Pro Sale. View Details »

Ship 10 Times Faster With These Designs

Ship 10 Times Faster With These Designs

Your competition will hate you. By the time they release one feature with a ton of bugs, you would have released 10 features with no bugs. A good design makes a world of difference in terms of ease of change, teamwork, testability, reliability and overall quality of life. I will present a software design approach that allowed my teams to ship unbelievably fast while keeping the quality above industry standards.

Anna Filina

February 25, 2021
Tweet

More Decks by Anna Filina

Other Decks in Programming

Transcript

  1. Anna Filina / @afilina • Coding since 1997. • PHP

    since 2003. • Legacy archaeology. • Test automation. • Talks and workshops. • YouTube videos. • Filina Consulting.
  2. Your competition will hate you. By the time they release

    one feature with a ton of bugs, you would have released 10 features with no bugs. A good design makes a world of difference in terms of ease of change, teamwork, testability, reliability and overall quality of life. I will present a software design approach that allowed my teams to ship unbelievably fast while keeping the quality above industry standards.
  3. final class CapturePaymentHandler { public function handle(ServerRequestInterface $request): ResponseInterface {

    $money = Money::fromRequest($request); $instrument = Instrument::fromRequest($request); $paymentId = $this->paymentProvider->charge($money, $instrument); } }
  4. final class CapturePaymentHandler { public function handle(ServerRequestInterface $request): ResponseInterface {

    $money = Money::fromRequest($request); $instrument = Instrument::fromRequest($request); $paymentId = $this->paymentProvider->charge($money, $instrument); return $this->createSuccessResponse($paymentId); } }
  5. Waiting for all the information before starting work. Postpone decisions

    using interfaces. Write code for the things that you do know. Validate assumptions using code. Uncover new information or use cases.
  6. class CapturePaymentHandler { public function handle(ServerRequestInterface $request): ResponseInterface { $money

    = Money::fromRequest($request); $instrument = Instrument::fromRequest($request); $paymentId = $this->paymentProvider->charge($money, $instrument); return $this->createSuccessResponse($paymentId); } }
  7. class CapturePaymentHandler { public function handle(ServerRequestInterface $request): ResponseInterface { $money

    = Money::fromRequest($request); $instrument = Instrument::fromRequest($request); $paymentId = $this->paymentProvider->charge($money, $instrument); return $this->createSuccessResponse($paymentId); } }
  8. class CapturePaymentHandler { public function handle(ServerRequestInterface $request): ResponseInterface { $money

    = Money::fromRequest($request); $instrument = Instrument::fromRequest($request); $paymentId = $this->paymentProvider->charge($money, $instrument); return $this->createSuccessResponse($paymentId); } }
  9. interface PaymentProvider { /** * @throws ChargeFailed */ public function

    charge(Money $money, Instrument $instrument): PaymentId; }
  10. $money = Money::fromRequest($request); $instrument = Instrument::fromRequest($request); try { $paymentId =

    $this->paymentProvider->charge($money, $instrument); } catch (ChargeFailed $exception) { return $this->createErrorResponse($exception->getMessage()); } return $this->createSuccessResponse($paymentId);
  11. Handlers + tests PaymentProvider implementation + tests Acceptance tests +

    wiring components Value objects + tests POST /payments/capture
  12. charge($money, $instrument) Money int $amount string $currency charge(int $amount, string

    $currency, string $cardNumber, string $cardExpiry, string $postalCode, …) Instrument Card $card Address $address
  13. Create Small Classes & Methods • 10 statements per method.

    • Classes that can fit in your head.
  14. We don't understand the domain. Separate the domain from the

    other layers. Create small classes and methods.
  15. final class CapturePaymentHandler { private PaymentProvider $paymentProvider; public function __construct(PaymentProvider

    $paymentProvider) { $this->paymentProvider = $paymentProvider; } } final class CapturePaymentHandler { private StripePaymentProvider $paymentProvider; public function __construct() { $this->paymentProvider = new StripePaymentProvider(); } }
  16. final class CapturePaymentHandlerTest extends TestCase { protected function setUp(): void

    { $this->paymentProvider = $this->createMock(PaymentProvider::class); $this->capturePaymentHandler = new CapturePaymentHandler( $this->paymentProvider ); } }