Confessions of a code poet

Stephan Hochdörfer // 18.01.2018

About me


  • Stephan Hochdörfer
  • Head of Technology, bitExpert AG
    (Mannheim, Germany)
  • S.Hochdoerfer@bitExpert.de
  • @shochdoerfer

  • #PHP, #DevOps, #Automation
  • #phpugffm, #phpugmrn, #unKonf






« Just because I know a language does not
make me Shakespeare! #coding » - @cj_harris5



function build_rand_array($array_size)
{
    $array = array();

    do {
        $random_number = rand();
        if (!in_array($random_number, $array)) {
            $array[] = $random_number;
        }
    } while (count($array) < $array_size);

    return $array;
}

Is coding art or craft?

Is coding art or craft?




« good programmers don't need to comment
their code, art is self explanatory » - @failnaut

No comments needed?


function build_rand_array(int $array_size): array
{
    $array = array();

    do {
        $random_number = rand();
        if (!in_array($random_number, $array)) {
            $array[] = $random_number;
        }
    } while (count($array) < $array_size);

    return $array;
}

No comments needed?

/**
 * Returns a one-dimensional, index-based array containing
 * random numbers.
 *
 * @param int $array_size
 * @return int[]
 */
function build_rand_array(int $array_size): array
{
    $array = array();

    do {
        $random_number = rand();
        if (!in_array($random_number, $array)) {
            $array[] = $random_number;
        }
    } while (count($array) < $array_size);

    return $array;
}

Code formatting style

Code formatting style


function build_rand_array($iArraySize)
{
    $aRandomArray = array();

    do
    {
        $iNumber = rand();
        if(!in_array($iNumber, $aRandomArray))
        {
            $aRandomArray[] = $iNumber;
        }
    }
    while(count($aRandomArray) < $iArraySize);

    return $aRandomArray;
}

Code formatting style


function build_rand_array($array_size)
{
    $array = array();

    do {
        $random_number = rand();
        if (!in_array($random_number, $array)) {
            $array[] = $random_number;
        }
    } while (count($array) < $array_size);

    return $array;
}

Code formatting style




« Do not invent your own coding standard.
Instead, focus on the important parts! » - Me

Code organization

./project
   |-bin
   |-config
   |-public
   |-src
   |---SomeApplication
   |-----Action
   |-------Module1
   |-------Module2
   |-----Dao
   |-------Module1
   |-------Module2
   |-----Service
   |-------Module1
   |-------Module2
   |-----Views
   |-------Module1
   |-------Module2

Code organization

./project
   |-bin
   |-config
   |-public
   |-src
   |---SomeApplication
   |-----Module1
   |-------Action
   |-------Dao
   |-------Service
   |-------Views
   |-----Module2
   |-------Action
   |-------Dao
   |-------Service
   |-------Views

Code organization




« Don’t organize code according to its job title (model, view, utility); sort it according to its purpose (feature). » - @jessitron

Code organization




« Keeping things together just because they are of same type,
instead of grouping them by domains, should be a number
one anti-pattern. Yet still it's the default of many frameworks
and people keep using forever. Please, stop! » - @bartsokol

Code organization

./project
   |-bin
   |-config
   |-public
   |-src
   |---Attendee
   |---Cli
   |---Day
   |---Http
   |---OAuth2
   |---Sync
   |---TalkProposal
   |---Talk
   |---Track

Code organization

./project
   |-src
   |---TalkProposal
   |-----Events
   |-----ListTalkProposals.php
   |-----ProposeTalk.php
   |-----RemoveTalkProposal.php
   |-----TalkProposal.php
   |-----TalkProposalAbstract.php
   |-----TalkProposalFinder.php
   |-----TalkProposalId.php
   |-----TalkProposalReadModel.php
   |-----TalkProposalRepository.php
   |-----TalkProposalTitle.php
   |-----UpdateTalkProposal.php

Code organization





« Organize code according to its purpose » - Me

What about my util package?

Code organization





« Core, common or util are no valid package names! » - Me

Naming things



« If you don't know what a thing should be called,
you cannot know what it is.

If you don't know what it is, you cannot sit down
and write the code. » - Sam Gardiner

Naming things





« LazyLoadingValueHolderFactory » - ProxyManager

Naming things





« AbstractSingletonProxyFactoryBean » - Spring Framework

Naming things




« Germans are uniquely qualified to work on
#SpringFramework code because even after
AbstractSingletonProxyFactoryBean we still have
half a breath left » - Oliver Gierke

Naming things




« Hauptversionsnummererhöhungsangst »

(fear of increasing the major version in semver)

Naming things





« Use names that clearly express their intent. » - Me

Naming things gone wrong!





« Microservices for the Frontend »

Naming things wrong!




« the container » - almost every DI library in PHP
(except Disco)

DI Config gone wrong

<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services 
    http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service id="dependency" class="\My\Dependency">
        </service>

        <service id="manager" class="\My\Manager">
            <call method="setDependency">
                <argument type="service" id="dependency" />
            </call>
        </service>
    </services>
</container>

DI Config gone wrong





« Is there a way to run a test that builds the
Symfony DIC and forces instantiation of all objects?
to weed out any errors? » - @rdohms

DI Config done right!




« [...] a PSR-11 compatible, annotation based
dependency injection container. » - Disco

DI Config done right!

<?php

use bitExpert\Disco\Annotations\Bean;
use bitExpert\Disco\Annotations\Configuration;

/** @Configuration */
class Config
{
    /** @Bean */
    protected function database() : Database
    {
        return new Database('mysql://user:secret@localhost/mydb');
    }
    /** @Bean */
    public function productService() : ProductService
    {
        return new ProductService($this->database());
    }
    /** @Bean */
    public function productController() : ProductController
    {
        return new ProductController($this->productService());
    }
}

Too many dependencies?

<?php

class ProductController extends Controller
{
    public function __construct(
        ProductService $productService,
        ProductImageService $productImageService,
        PricingService $pricingService,
        CustomerService $customerService,
        BasketService $basketService,
        // [...]
    ) {
        // [...]
    }
}

ContainerAware is a lie!

<?php

interface ContainerAware
{
    public function setContainer(ContainerInterface $container);
}
<?php

class ProductController extends Controller
    implements ContainerAware
{
    protected $productService;

    public function setContainer(ContainerInterface $container)
    {
        $this->productService = $container->get('productService');
    }
}

Is it really a dependency?

<?php

class ProductController extends Controller
{
    protected $productService;

    public function __construct(ContainerInterface $container)
    {
        $this->productService = $container->get('productService');
        $this->productImgService = $container->get('productImgService');
        $this->pricingService = $container->get('pricingService');
        $this->customerService = $container->get('customerService');
        $this->basketService = $container->get('basketService');
    }
}

What is a dependency?

Is a logger a dependency?

Is a logger a dependency?

<?php

class ProductController extends Controller
{
    protected $logger;

    public function __construct()
    {
        $this->logger = new Logger('/tmp/application.log');
        // [...]
    }
}
{
    "require": {
        "psr/log": "^1.0",
        "monolog/monolog": "^1.21"
    }
}

LoggerAwareInterface?

<?php

namespace Psr\Log;

/**
 * Describes a logger-aware instance.
 */
interface LoggerAwareInterface
{
    /**
     * Sets a logger instance on the object.
     *
     * @param LoggerInterface $logger
     * @return null
     */
    public function setLogger(LoggerInterface $logger);
}

Constructor injection maybe?

<?php

class ProductController extends Controller
{
    protected $logger;

    public function __construct(\Psr\Log\LoggerInterface $logger)
    {
        $this->logger = $logger;
        // [...]
    }
}

Simple Logging Facade




« To achieve true interoperability, your own code should not depend on a specific library implementing the PSR-3. » - slf4psrlog

bitexpert/slf4psrlog

$> composer.phar require bitexpert/slf4psrlog
<?php

class ProductController extends Controller
{
    protected $logger;

    public function __construct()
    {
        $this->logger = \bitExpert\Slf4PsrLog\LoggerFactory::getLogger(
            __CLASS__
        );
        // [...]
    }
}

The glue code...

<?php

\bitExpert\Slf4PsrLog\LoggerFactory::registerFactoryCallback(
    function($channel) {
        if (!\Monolog\Registry::hasLogger($channel)) {
            \Monolog\Registry::addLogger(
                new \Monolog\Logger($channel)
            );
        }

        return \Monolog\Registry::getInstance($channel);
    }
);

Simple Logging Facade




« A logger is part of your infrastructure
and not a dependency of your class. » - Me

What about Interfaces?

What about Interfaces?




« Interfaces are not an overhead,
they help to structure your code. » - Me

Interfaces done wrong!

<?php

class ProductRepository
{
}
<?php

class ProductRepository implements ProductRepositoryInterface
{
}
<?php

class ProductController extends Controller
{
    public function __construct(ProductRepositoryInterface $repository)
    {
        // [...]
    }
}

Interfaces done right!

<?php

interface ProductRepository
{
}
<?php

class PostgreSqlProductRepository implements ProductRepository
{
}
<?php

class XmlFileStorageProductRepository implements ProductRepository
{
}

Wrong Interface placement


./project
   |-bin
   |-config
   |-public
   |-src
   |---ProductCatalog
   |-----Action
   |-----Repository
   |-------ProductRepository.php
   |-------PostgreSqlProductRepository.php
   |-------XmlFileStorageProductRepository.php

Right Interface placement


./project
   |-bin
   |-config
   |-public
   |-src
   |---ProductCatalog
   |-----ListProducts.php
   |-----ProductDetails.php
   |-----ProductRepository.php
   |---PostgreSqlBackend
   |-----PostgreSqlProductRepository.php
   |---XmlFileBackend
   |-----XmlFileStorageProductRepository.php

And finally...





« Use `final` in your code. Wherever you can. » - Me

Finalize your models

<?php

final class Customer extends AggregateRoot
{
    /** @var CustomerId */
    private $customerId;
    /** @var string */
    private $firstname;
    /** @var string */
    private $lastname;
    /** @var EmailAddress */
    private $emailAddress;
    /** @var PasswordHash */
    private $password;

    // [...]
}

Finalize your events

<?php

final class PasswordWasChanged extends AggregateChanged
{
    /** @var  CustomerId */
    private $customerId;
    /** @var PasswordHash */
    private $passwordHash;

    // [...]
}

Finalize your Value objects

<?php

final class EmailAddress implements ValueObject
{
    /** @var string */
    private $email;

    // [...]
}

Finalize your repositories

<?php

final class CustomerRepository extends AggregateRepository
{
    /**
     * Persists given $customer in the repository.
     *
     * @param Customer $customer
     */
    public function save(Customer $customer): void
    {
        $this->saveAggregateRoot($customer);
    }

    // [...]
}

Back to the basics




« I have a problem with introducing junior developers
to web dev using massive tooling setup where
the whole web platform is abstracted away. »
- @Lady_Ada_King

Focus on good code




« More good code has been written in languages
denounced as bad than in languages proclaimed
wonderful - much more. » - Bjarne Stroustrup

Thank you! Questions?


  • Stephan Hochdörfer
  • Head of Technology, bitExpert AG
    (Mannheim, Germany)
  • S.Hochdoerfer@bitExpert.de
  • @shochdoerfer

  • #PHP, #DevOps, #Automation
  • #phpugffm, #phpugmrn, #unKonf