Dietro le quinte di Tom's Hardware

ECCENTRIC DEVELOPER

Oscar Fanelli

Engineering Manager

Zend Framework / Symfony

Doctrine

ReactJS

Docker

PhpStorm

Faccio cose

Conosco gente

Stringo mani

Pigio sulla tastiera

Infrastructure

(italian) Legal limitations

Provider

Cache

  • Varnish
    • Application is not touched
    • No bottlenecks
    • Efficient
    • Plain
  • Redis
    • Customizable
    • Scalable
    • Scales third party services' requests
    • Database relief

Cloudflare

  • DDOS attacks
  • Assets CDN
  • Redirects
  • HTTPS

Deploy

  • VPN
  • Zero downtime
    • ... (the heaven) ...
  • Rollback
  • Flush cache
  • One-line command

Actors

Actors

  • CEO
  • COO
  • Director
  • Provider
  • SEO
  • Designer
  • Editors
  • Sales
  • Video-makers
  • Purch
  • Lawyer
  • Google / Facebook
  • Forum Manager
  • Forum Moderators
  • Blog authors
  • Partner
  • USERS

Third-party Services

Third-party services

$$$

ADV

$$$

AD placements

Back-office AD tools

AD placements

"The Coalition for Better Ads will leverage consumer insights and cross-industry expertise to develop and implement new global standards for online advertising that address consumer expectations."

Merchant

$$$

AdBlock

$$$

About AdBlock

  • ~60% of users use AdBlock
  • Let the user use AdBlock without any warning

Custom anti-AdBlock strategy

  • Code injected on render (no JS async call)
  • Dynamic CSS classes
  • Unpredictable CSS classes
  • CSS style injected on the same page

Back-office

Golden rules

  • Things will change, very fast: be prepared
  • Standardize UI (use fxxxxxg Bootstrap)
  • MUST be easy to:
    • Add/edit/remove fields on Entities
    • Add/edit/remove fields on forms
    • Add/remove CRUD Entities' panels
  • Listen to who will use it
  • Identify common flows and optimise the navigation
  • Avoid internal stats (if you are not sure about them)

{The Code}

w(h)ell yes, sometimes we write some lines of code

SEO

TTFB and more

  • Speed is SEO-friendly
  • ~70% users use mobile connection
  • Lazy loading
  • If you can't optimise speed, at least optimise cache

Third-party JS

Third-party JS

  • Server-side rendering
  • Prefer GTM/Segment.io integration
  • Time spent with the partner's tech team is well spent

Traits

  • Dirty stuff
  • Don't use traits
  • BUT if you really need them, USE them
/**
 * An Entity that implement VisitableTrait has a visit counter
 * Needed an additional implementation of the job in the ControllerFactory and in the Controller
 */
trait VisitableTrait
{
    /**
     * @ORM\Column(type="integer")
     * @var int
     */
    protected $visits = 0;

    public function visit()
    {
        $this->visits++;
    }

    /**
     * @return int
     */
    public function getVisits()
    {
        return $this->visits;
    }

    /**
     * @param int $visits
     */
    public function setVisits($visits)
    {
        $this->visits = $visits;
    }
}

Interfaces

  • Spread standards around your team
  • Avoid common mistakes
  • Do not replace docs
  • If you can't init with them, at least implement them later

Golden rules

  • Things will change, very fast: be prepared
  • Standardize UI (use fxxxxxg Bootstrap)
  • MUST be easy to:
    • Add/edit/remove fields on Entities
    • Add/edit/remove fields on forms
    • Add/remove CRUD Entities' panels
  • Listen to who will use it
  • Identify common flows and optimise the navigation
  • Avoid internal stats (if you are not sure about them)

Back-office

DRY

  • Help your team
  • You are forced to create a well-structured codebase
  • If you have to be quick, at least use traits

Dependency Injection

  • More reusable code
  • More readable code
  • Easy unit-testing
class ContentControllerFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $controllerManager)
    {
        $parentLocator = $controllerManager->getServiceLocator();

        /** @var EntityManager $em */
        $em = $parentLocator->get('Doctrine\ORM\EntityManager');

        /** @var ContentService $contentService */
        $contentService = $parentLocator->get('Toms\Service\ContentService');

        /** @var ContentViewService $contentViewService */
        $contentViewService = $parentLocator->get('Toms\Service\ContentViewService');

        /** @var EmailService $emailService */
        $emailService = $parentLocator->get('Toms\Service\EmailService');

        return new Controller($em, $contentService, $contentViewService, $emailService);
    }
}
/**
 * @param EntityManager $em
 * @param ContentService $contentService
 * @param ContentViewService $contentViewService
 * @param EmailService $emailService
 */
public function __construct(EntityManager $em, ContentService $contentService, ContentViewService $contentViewService, EmailService $emailService)
{
    parent::__construct($em);

    $this->contentService = $contentService;
    $this->contentViewService = $contentViewService;
    $this->emailService = $emailService;
}

Configuration files

  • Readable
  • Easy to understand by your team and the future-you
  • Easy to find
  • Feature toggling

Feature toggling

  • PHP Entity
  • Redis
  • Configuration file

Assets

  • Compile via PHP at runtime
  • Use cache
  • Do not version compiled assets
  • Avoid watchers
    • Hard to maintain
    • Cross-OS troubles

RWOverdijk/AssetManager

'filters' => [
    'css' => [
        ['filter' => 'ScssphpFilter'],
        getenv('CACHE') && getenv('CACHE_ASSETS')
            ? ['service' => __NAMESPACE__ . '\Filter\UglifyCssFilter']
            : null,
    ],
    'js'  => [
        getenv('CACHE') && getenv('CACHE_ASSETS')
            ? ['service' => __NAMESPACE__ . '\Filter\UglifyJsFilter']
            : null,
    ],
],

Queues

  • You are lazy, find an easy-to-configure queues service
  • Think about a different DB (maybe NoSQL?)

juriansluiman/SlmQueueDoctrine

'job_manager' => [
    'factories' => [
        'Toms\Job\VisitJob'               => 'Toms\JobFactory\VisitJobFactory',
        'Toms\Job\ShareOnSocialJob'       => 'Toms\JobFactory\ShareOnSocialJobFactory',
        'Toms\Job\UpdateEpriceProductJob' => 'Toms\JobFactory\UpdateEpriceProductJobFactory',
    ],
],

Debug

  • Search for a live-debugging strategy
  • Don't fxxxxxg edit via FTP
  • But if you are in a hurry, at least think about a rollback strategy

Cronjobs

  • Every job should be managed by your application
  • Version-control
  • When you add a cronjob, explicit it in the PR description

Deploy

  • Every task should be managed by your application
  • No extra actions before/after deploy
  • Search for a one-line deploy strategy
  • Keep your company updated
    • Release notes on Slack

Release notes for lazy guys

  • Automatic creation of a PR from develop --> master
  • Automatic creation of releases notes based on your diff commits
php public/index.php github create-minor-release

Lesson learned

Learn to lie

can you do it?

For complaints

oscar.fanelli@gmail.com

@nesis

@pensiero

Dietro le quinte di Tom's Hardware

By Oscar Fanelli

Dietro le quinte di Tom's Hardware

  • 1,671