$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
PRO

February 25, 2021
Tweet

More Decks by Anna Filina

Other Decks in Programming

Transcript

  1. Ship 10 Times Faster
    With These Designs
    CONFOO 2021 | FEB 25, 2021
    @afilina

    View Slide

  2. Anna Filina / @afilina
    • Coding since 1997.
    • PHP since 2003.
    • Legacy archaeology.
    • Test automation.
    • Talks and workshops.
    • YouTube videos.
    • Filina Consulting.

    View Slide

  3. 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.

    View Slide

  4. Waiting for all the information before starting work.

    View Slide

  5. Knowns
    • Payment Provider
    • Charge money
    • Refund money of a previous transaction

    View Slide

  6. Payment Provider
    charge

    View Slide

  7. Payment Provider
    charge (Money, Instrument)
    refund

    View Slide

  8. Payment Provider
    charge (Money, Instrument)
    refund (Money, PaymentId)

    View Slide

  9. Payment Provider
    charge (Money, Instrument) : PaymentId
    refund (Money, PaymentId)

    View Slide

  10. Payment Provider
    charge (Money, Instrument) : PaymentId
    refund (Money, PaymentId) : RefundId

    View Slide

  11. interface PaymentProvider
    {
    public function charge(Money $money, Instrument $instrument): PaymentId;
    }

    View Slide

  12. Are we able to use this interface?

    View Slide

  13. Submit
    Form field
    Form field
    POST
    /payments/capture

    View Slide

  14. Validate input
    Controller
    Charge via PaymentProvider
    Request Response
    Send output

    View Slide

  15. final class CapturePaymentHandler
    {
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
    }
    }

    View Slide

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

    View Slide

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

    View Slide

  18. 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);
    }
    }

    View Slide

  19. {
    "instrument": {
    //...
    },
    "money": {
    "currency": "USD",
    "amount": 1050
    }
    }
    {
    "paymentId": "CC-0001"
    }

    View Slide

  20. FE/BE Contract
    • Is input viable?
    • Is output viable?
    • Talk use cases.

    View Slide

  21. 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.

    View Slide

  22. Can't have multiple developers on one feature.

    View Slide

  23. 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);
    }
    }

    View Slide

  24. 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);
    }
    }

    View Slide

  25. 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);
    }
    }

    View Slide

  26. interface PaymentProvider
    {
    /**
    * @throws ChargeFailed
    */
    public function charge(Money $money, Instrument $instrument): PaymentId;
    }

    View Slide

  27. $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);

    View Slide

  28. Handlers + tests
    PaymentProvider implementation + tests
    Acceptance tests + wiring components
    Value objects + tests
    POST
    /payments/capture

    View Slide

  29. Can't have multiple developers on one feature.
    Create separate classes with clear contracts.

    View Slide

  30. We don't understand the domain.

    View Slide

  31. charge($money, $instrument)
    Money
    int $amount
    string $currency
    charge(int $amount, string $currency, string $cardNumber,
    string $cardExpiry, string $postalCode, …)
    Instrument
    Card $card
    Address $address

    View Slide

  32. PaymentProvider
    HttpClient
    send (Request)
    charge (Money, Instrument)

    View Slide

  33. Money
    Instrument
    PaymentProvider
    Domain

    View Slide

  34. Money
    Instrument
    PaymentProvider
    CapturePaymentHandler Application

    View Slide

  35. Money
    Instrument
    PaymentProvider
    CapturePaymentHandler
    HttpClient Infrastructure

    View Slide

  36. Create Small Classes & Methods
    • 10 statements per method.
    • Classes that can fit in your head.

    View Slide

  37. We don't understand the domain.
    Separate the domain from the other layers.
    Create small classes and methods.

    View Slide

  38. Bugs.

    View Slide

  39. Money
    Instrument
    PaymentProvider
    CapturePaymentHandler
    HttpClient
    Unit tests
    Unit tests
    Integration tests
    Acceptance
    tests

    View Slide

  40. final class CapturePaymentHandler
    {
    private StripePaymentProvider $paymentProvider;
    public function __construct()
    {
    $this->paymentProvider = new StripePaymentProvider();
    }
    }

    View Slide

  41. 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();
    }
    }

    View Slide

  42. final class CapturePaymentHandlerTest extends TestCase
    {
    protected function setUp(): void
    {
    $this->paymentProvider = $this->createMock(PaymentProvider::class);
    $this->capturePaymentHandler = new CapturePaymentHandler(
    $this->paymentProvider
    );
    }
    }

    View Slide

  43. Bugs.
    Just write tests.
    Learn to write even better tests.

    View Slide

  44. @afilina

    View Slide