Min-Maxing Software Costs - Laracon EU 2015

95
Min-Maxing Software Costs

Transcript of Min-Maxing Software Costs - Laracon EU 2015

Page 1: Min-Maxing Software Costs - Laracon EU 2015

Min-Maxing Software Costs

Page 2: Min-Maxing Software Costs - Laracon EU 2015

@everzet

Page 3: Min-Maxing Software Costs - Laracon EU 2015

@inviqa

Page 4: Min-Maxing Software Costs - Laracon EU 2015

What is this talk about?

Page 5: Min-Maxing Software Costs - Laracon EU 2015

Harm that "Laravel Facades" inflict on not-suspecting

developers.

Page 6: Min-Maxing Software Costs - Laracon EU 2015

Bad idea that is Active Record and Eloquent.

Page 7: Min-Maxing Software Costs - Laracon EU 2015

Other framework's superiority over Laravel.

Page 8: Min-Maxing Software Costs - Laracon EU 2015

Killing kittens.

Page 9: Min-Maxing Software Costs - Laracon EU 2015

And other subjective and nonconstructive crap like that ...

Page 10: Min-Maxing Software Costs - Laracon EU 2015

... is not in this talk.

Page 11: Min-Maxing Software Costs - Laracon EU 2015

Actually in this talk

1. Introducing & making sense of development costs

2. Highlighting the context of tools & practices we use

3. Years of observation & experience, not data collection and analysis

Page 12: Min-Maxing Software Costs - Laracon EU 2015

Context, the talk

Page 13: Min-Maxing Software Costs - Laracon EU 2015

Software Costs

Page 14: Min-Maxing Software Costs - Laracon EU 2015

Software Costs

Page 15: Min-Maxing Software Costs - Laracon EU 2015

Software Costs

1. Time to write & test code

2. Time to change code & tests

3. Time to refactor code & tests

Page 16: Min-Maxing Software Costs - Laracon EU 2015

Software Costs

1. Time to write & test code - Cost of Introduction

2. Time to change code & tests - Cost of Change

3. Time to refactor code & tests - Cost of Ownership

Page 17: Min-Maxing Software Costs - Laracon EU 2015

Cost of Introduction

Page 18: Min-Maxing Software Costs - Laracon EU 2015

Cost of IntroductionTime it takes to introduce new,

naturally independent application logic.

Page 19: Min-Maxing Software Costs - Laracon EU 2015

Attributes

— Has direct correlation to business value

— Has direct correlation to LOC

— Relatively easy to optimise by generalisation

Page 20: Min-Maxing Software Costs - Laracon EU 2015

Dynamics

— Visible from the outset

— Loses relevancy over the project lifetime

— Stable across projects

Page 21: Min-Maxing Software Costs - Laracon EU 2015

Dynamics

— Visible from the outset

— Loses relevancy over the project lifetime

— Stable across projects

Page 22: Min-Maxing Software Costs - Laracon EU 2015

Cost of Introduction is relatively easy to optimise.

Page 23: Min-Maxing Software Costs - Laracon EU 2015

Optimising for CoI: Convenience Layer

Page 24: Min-Maxing Software Costs - Laracon EU 2015

Service Locator

// --- Explicit Dependency

public function __construct(Cache $cache) { $this->cache = $cache;}

public function index() { $photos = $this->cache->get('photos'); // ...}

// --- "Laravel Facade"

public function index() { $photos = Cache::get('photos'); // ...}

Page 25: Min-Maxing Software Costs - Laracon EU 2015

Base Class

// --- Base Controller

class MyController extends Controller{ public function indexAction() { $homepageUrl = $this->generateUrl('homepage'); // ... }}

Page 26: Min-Maxing Software Costs - Laracon EU 2015

Optimising for CoI: Generalisation

Page 27: Min-Maxing Software Costs - Laracon EU 2015

Active Record

// --- Custom Mapping

class DbalCustomerRepository implements CustomerRepository{ public function findCustomerWithName($name) { // ... }}

// --- Eloquent

use Illuminate\Database\Eloquent\Model;

class Customer extends Model{ // ...}

Page 28: Min-Maxing Software Costs - Laracon EU 2015

Event Dispatcher

// --- Event Subscriber

interface MyListener { public function emailWasSent($email, $text);}

// ...

public function sendEmail() { // ... $this->myListenerInstance->emailWasSent($email, $text);}

// --- Event Dispatcher

$eventDispatcher->dispatch('email.sent', new Event($email, $text));

Page 29: Min-Maxing Software Costs - Laracon EU 2015

Dependency Injection Container

// --- Dependency Inversion Principle

$contoller = new MyController( new Router(), new Cache(new Configuration()));

// --- Dependency Injection Container

$controller = $container->get('controller.my');

Page 30: Min-Maxing Software Costs - Laracon EU 2015

No matter what you think, optimising for CoI (Cost of

Introduction) is not inherently a bad thing.

Page 31: Min-Maxing Software Costs - Laracon EU 2015

A Cut-Off of the product

Page 32: Min-Maxing Software Costs - Laracon EU 2015

If the product life is short enough to not encounter loss of CoI

relevancy, then the CoI is the only cost worth optimising for.

Page 33: Min-Maxing Software Costs - Laracon EU 2015

Convenience based projects either die a hero or live long

enough to see themselves become the villain.

Page 34: Min-Maxing Software Costs - Laracon EU 2015

Cost of Change

Page 35: Min-Maxing Software Costs - Laracon EU 2015

Cost of ChangeTime it takes to adapt the

existing application logic to new business realities.

Page 36: Min-Maxing Software Costs - Laracon EU 2015

Attributes

— Has direct correlation to business value

— Has no direct correlation to LOC

— Affected by generalisation

Page 37: Min-Maxing Software Costs - Laracon EU 2015

Dynamics

— Invisible from the outset

— Gains relevancy during the project lifetime

— Exponentially increases over time

Page 38: Min-Maxing Software Costs - Laracon EU 2015

Cost of Change increases exponentially over time.

Page 39: Min-Maxing Software Costs - Laracon EU 2015

public function searchAction(Request $req){ $form = $this->createForm(new SearchQueryType, new SearchQuery); $normalizedOrderBys = $this->getNormalizedOrderBys($filteredOrderBys); $this->computeSearchQuery($req, $filteredOrderBys); if ($req->query->has('search_query')) { /** @var $solarium \Solarium_Client */ $solarium = $this->get('solarium.client'); $select = $solarium->createSelect(); // configure dismax $dismax = $select->getDisMax(); $dismax->setQueryFields(array('name^4', 'description', 'tags', 'text', 'text_ngram', 'name_split^2')); if ($req->query->has('search_query')) { $form->bind($req); if ($form->isValid()) { $escapedQuery = $select->getHelper()->escapeTerm($form->getData()->getQuery()); $escapedQuery = preg_replace('/(^| )\\\\-(\S)/', '$1-$2', $escapedQuery); $escapedQuery = preg_replace('/(^| )\\\\\+(\S)/', '$1+$2', $escapedQuery); if ((substr_count($escapedQuery, '"') % 2) == 0) { $escapedQuery = str_replace('\\"', '"', $escapedQuery); } $select->setQuery($escapedQuery); } } } elseif ($req->getRequestFormat() === 'json') { return JsonResponse::create(array( 'error' => 'Missing search query, example: ?q=example' ), 400)->setCallback($req->query->get('callback')); } return $this->render('SomeAppWebBundle:Web:search.html.twig');}

Page 40: Min-Maxing Software Costs - Laracon EU 2015

public function searchAction(Request $req){ $form = $this->createForm(new SearchQueryType, new SearchQuery); $this->computeSearchQuery($req, $filteredOrderBys); $typeFilter = $req->query->get('type'); if ($req->query->has('search_query') || $typeFilter) { /** @var $solarium \Solarium_Client */ $solarium = $this->get('solarium.client'); $select = $solarium->createSelect(); // configure dismax $dismax = $select->getDisMax(); $dismax->setQueryFields(array('name^4', 'description', 'tags', 'text', 'text_ngram', 'name_split^2')); $dismax->setPhraseFields(array('description')); $dismax->setBoostFunctions(array('log(trendiness)^10')); $dismax->setMinimumMatch(1); $dismax->setQueryParser('edismax'); // filter by type if ($typeFilter) { $filterQueryTerm = sprintf('type:"%s"', $select->getHelper()->escapeTerm($typeFilter)); $filterQuery = $select->createFilterQuery('type')->setQuery($filterQueryTerm); $select->addFilterQuery($filterQuery); } if ($req->query->has('search_query')) { $form->bind($req); if ($form->isValid()) { $escapedQuery = $select->getHelper()->escapeTerm($form->getData()->getQuery()); $escapedQuery = preg_replace('/(^| )\\\\-(\S)/', '$1-$2', $escapedQuery); $escapedQuery = preg_replace('/(^| )\\\\\+(\S)/', '$1+$2', $escapedQuery); if ((substr_count($escapedQuery, '"') % 2) == 0) { $escapedQuery = str_replace('\\"', '"', $escapedQuery); } $select->setQuery($escapedQuery); } } $paginator = new Pagerfanta(new SolariumAdapter($solarium, $select)); $perPage = $req->query->getInt('per_page', 15); if ($perPage <= 0 || $perPage > 100) { if ($req->getRequestFormat() === 'json') { return JsonResponse::create(array( 'status' => 'error', 'message' => 'The optional packages per_page parameter must be an integer between 1 and 100 (default: 15)', ), 400)->setCallback($req->query->get('callback')); } $perPage = max(0, min(100, $perPage)); } } elseif ($req->getRequestFormat() === 'json') { return JsonResponse::create(array( 'error' => 'Missing search query, example: ?q=example' ), 400)->setCallback($req->query->get('callback')); } return $this->render('SomeAppWebBundle:Web:search.html.twig');}

Page 41: Min-Maxing Software Costs - Laracon EU 2015

public function searchAction(Request $req){ $form = $this->createForm(new SearchQueryType, new SearchQuery); $filteredOrderBys = $this->getFilteredOrderedBys($req); $normalizedOrderBys = $this->getNormalizedOrderBys($filteredOrderBys); $this->computeSearchQuery($req, $filteredOrderBys); $typeFilter = $req->query->get('type'); $tagsFilter = $req->query->get('tags'); if ($req->query->has('search_query') || $typeFilter || $tagsFilter) { /** @var $solarium \Solarium_Client */ $solarium = $this->get('solarium.client'); $select = $solarium->createSelect(); // configure dismax $dismax = $select->getDisMax(); $dismax->setQueryFields(array('name^4', 'description', 'tags', 'text', 'text_ngram', 'name_split^2')); $dismax->setPhraseFields(array('description')); $dismax->setBoostFunctions(array('log(trendiness)^10')); $dismax->setMinimumMatch(1); $dismax->setQueryParser('edismax'); // filter by type if ($typeFilter) { $filterQueryTerm = sprintf('type:"%s"', $select->getHelper()->escapeTerm($typeFilter)); $filterQuery = $select->createFilterQuery('type')->setQuery($filterQueryTerm); $select->addFilterQuery($filterQuery); } // filter by tags if ($tagsFilter) { $tags = array(); foreach ((array) $tagsFilter as $tag) { $tags[] = $select->getHelper()->escapeTerm($tag); } $filterQueryTerm = sprintf('tags:("%s")', implode('" AND "', $tags)); $filterQuery = $select->createFilterQuery('tags')->setQuery($filterQueryTerm); $select->addFilterQuery($filterQuery); } if (!empty($filteredOrderBys)) { $select->addSorts($normalizedOrderBys); } if ($req->query->has('search_query')) { $form->bind($req); if ($form->isValid()) { $escapedQuery = $select->getHelper()->escapeTerm($form->getData()->getQuery()); $escapedQuery = preg_replace('/(^| )\\\\-(\S)/', '$1-$2', $escapedQuery); $escapedQuery = preg_replace('/(^| )\\\\\+(\S)/', '$1+$2', $escapedQuery); if ((substr_count($escapedQuery, '"') % 2) == 0) { $escapedQuery = str_replace('\\"', '"', $escapedQuery); } $select->setQuery($escapedQuery); } } $paginator = new Pagerfanta(new SolariumAdapter($solarium, $select)); $perPage = $req->query->getInt('per_page', 15); if ($perPage <= 0 || $perPage > 100) { if ($req->getRequestFormat() === 'json') { return JsonResponse::create(array( 'status' => 'error', 'message' => 'The optional packages per_page parameter must be an integer between 1 and 100 (default: 15)', ), 400)->setCallback($req->query->get('callback')); } $perPage = max(0, min(100, $perPage)); } $paginator->setMaxPerPage($perPage); $paginator->setCurrentPage($req->query->get('page', 1), false, true); $metadata = array(); foreach ($paginator as $package) { if (is_numeric($package->id)) { $metadata['downloads'][$package->id] = $package->downloads; $metadata['favers'][$package->id] = $package->favers; } } if ($req->getRequestFormat() === 'json') { try { $result = array( 'results' => array(), 'total' => $paginator->getNbResults(), ); } catch (\Solarium_Client_HttpException $e) { return JsonResponse::create(array( 'status' => 'error', 'message' => 'Could not connect to the search server', ), 500)->setCallback($req->query->get('callback')); } return JsonResponse::create($result)->setCallback($req->query->get('callback')); } if ($req->isXmlHttpRequest()) { try { return $this->render('PackagistWebBundle:Web:search.html.twig', array( 'packages' => $paginator, 'meta' => $metadata, 'noLayout' => true, )); } catch (\Twig_Error_Runtime $e) { if (!$e->getPrevious() instanceof \Solarium_Client_HttpException) { throw $e; } return JsonResponse::create(array( 'status' => 'error', 'message' => 'Could not connect to the search server', ), 500)->setCallback($req->query->get('callback')); } } return $this->render('PackagistWebBundle:Web:search.html.twig', array( 'packages' => $paginator, 'meta' => $metadata, )); } elseif ($req->getRequestFormat() === 'json') { return JsonResponse::create(array( 'error' => 'Missing search query, example: ?q=example' ), 400)->setCallback($req->query->get('callback')); } return $this->render('PackagistWebBundle:Web:search.html.twig');}

Page 42: Min-Maxing Software Costs - Laracon EU 2015

Exponential increase of Cost of Change is not inherently a problem of every product.

Page 43: Min-Maxing Software Costs - Laracon EU 2015

A Cut-Off of the product

Page 44: Min-Maxing Software Costs - Laracon EU 2015

If the product life is long enough to encounter exponential growth of CoC, then the CoC is the cost

worth optimising for.

Page 45: Min-Maxing Software Costs - Laracon EU 2015

If you want to change the world with your product, then the

change is the primary thing your product must prepare for.

Page 46: Min-Maxing Software Costs - Laracon EU 2015

Optimising for Cost of Introduction in most cases has a

negative effect on the Cost of Change curve.

Page 47: Min-Maxing Software Costs - Laracon EU 2015

That's why some engineers try to increase the Cost of Introduction

in attempt to affect the Cost of Change curve.

Page 48: Min-Maxing Software Costs - Laracon EU 2015

Cost of Introduction and Change

Page 49: Min-Maxing Software Costs - Laracon EU 2015

Increasing Cost of Introduction

Page 50: Min-Maxing Software Costs - Laracon EU 2015

Cost of Change with increased Cost of Introduction

Page 51: Min-Maxing Software Costs - Laracon EU 2015

Upfront Design (aka Waterfall) Illusion that one can control

cost of change by applying enough analysis upfront.

Page 52: Min-Maxing Software Costs - Laracon EU 2015

Upfront Design fails to achieve long-lasting effect because both

rate and nature of change for arbitrary domain is

unpredictable.

Page 53: Min-Maxing Software Costs - Laracon EU 2015

Cost of Ownership

Page 54: Min-Maxing Software Costs - Laracon EU 2015

Cost of OwnershipTime it takes to maintain the

owned application logic to support its ongoing change.

Page 55: Min-Maxing Software Costs - Laracon EU 2015

Attributes

— Intermediate between Cost of Introduction & Cost of Change

— Has no direct correlation to business value

— Has direct correlation to LOC

Page 56: Min-Maxing Software Costs - Laracon EU 2015

Dynamics

— Always invisible

— Always relevant

— Stable over time, but adds up

Page 57: Min-Maxing Software Costs - Laracon EU 2015

Cost of Ownership is the cost you pay for the right to change a

particular part (module, class, method) of application

continuosly and sustainably.

Page 58: Min-Maxing Software Costs - Laracon EU 2015

Testing

Page 59: Min-Maxing Software Costs - Laracon EU 2015

Unit testing

Page 60: Min-Maxing Software Costs - Laracon EU 2015

Refactoring

Page 61: Min-Maxing Software Costs - Laracon EU 2015

Introducing Cost of Ownership allows you to balance two other

costs.

Page 62: Min-Maxing Software Costs - Laracon EU 2015

Cost of Introduction and Change

Page 63: Min-Maxing Software Costs - Laracon EU 2015

Cost of Introduction and Ownership

Page 64: Min-Maxing Software Costs - Laracon EU 2015

Cost of Ownership effect on Cost of Change curve

Page 65: Min-Maxing Software Costs - Laracon EU 2015

Emergent DesignUsual result of ongoing

ownership.

Page 66: Min-Maxing Software Costs - Laracon EU 2015

Cost of Ownership of everything

Page 67: Min-Maxing Software Costs - Laracon EU 2015

Cost of Ownership of everything

Page 68: Min-Maxing Software Costs - Laracon EU 2015

Owning everything fails to achieve ever-increasing benefits,

because you rarely need to change the entire system.

Page 69: Min-Maxing Software Costs - Laracon EU 2015

End-to-end testing is owning everything

Page 70: Min-Maxing Software Costs - Laracon EU 2015

Cost of Ownership of the wrong thing

Page 71: Min-Maxing Software Costs - Laracon EU 2015

Ownership wouldn't help if you're owning the wrong thing.

Page 72: Min-Maxing Software Costs - Laracon EU 2015

Exclusive end-to-end testing is owning the wrong thing

Page 73: Min-Maxing Software Costs - Laracon EU 2015

You do want to own everything worth owning.

Page 74: Min-Maxing Software Costs - Laracon EU 2015

But you don't know what's worth owning at the beginning of the

project.

Page 75: Min-Maxing Software Costs - Laracon EU 2015

Software Costs recap

1. Cost of Introduction - Linear. Relevant at the beginning. Very easy to optimise for.

2. Cost of Change - Exponential. Relevant everywhere except the beginning. Hard to optimise for.

3. Cost of Ownership - Linear. Relevant throughout. Owning the wrong thing is bad.

Page 76: Min-Maxing Software Costs - Laracon EU 2015

Gaming Software Costs

Page 77: Min-Maxing Software Costs - Laracon EU 2015

Own only the logic you need to change.

Page 78: Min-Maxing Software Costs - Laracon EU 2015

Write only the logic you need to own.

Page 79: Min-Maxing Software Costs - Laracon EU 2015

Own everything you write.

Page 80: Min-Maxing Software Costs - Laracon EU 2015

Own everything you write.Try to not write anything.

Page 81: Min-Maxing Software Costs - Laracon EU 2015

Own everything you write.Try to not write anything.

Reuse everything else.

Page 82: Min-Maxing Software Costs - Laracon EU 2015

1. Document the need

Page 83: Min-Maxing Software Costs - Laracon EU 2015

2. Spike - Experiment with tools available

Page 84: Min-Maxing Software Costs - Laracon EU 2015

3. Document changes & constraints

Page 85: Min-Maxing Software Costs - Laracon EU 2015

4. Stabilise - Claim ownership when the thing grows outside of tool boundaries

Page 86: Min-Maxing Software Costs - Laracon EU 2015

5. Isolate Religiously

Page 87: Min-Maxing Software Costs - Laracon EU 2015

Steps

1. Document the need

2. Spike

3. Document changes & constraints

4. Stabilise

5. Isolate Religiously

Page 88: Min-Maxing Software Costs - Laracon EU 2015

Unit testing is owning

Page 89: Min-Maxing Software Costs - Laracon EU 2015

Refactoring is owning

Page 90: Min-Maxing Software Costs - Laracon EU 2015

Test Driven Development is an ownership technique

Page 91: Min-Maxing Software Costs - Laracon EU 2015
Page 92: Min-Maxing Software Costs - Laracon EU 2015

Gaming Software Costs

1. Document

2. Spike & Stabilise

3. Use TDD for stabilisation

Page 93: Min-Maxing Software Costs - Laracon EU 2015

CC credits

- money.jpg - https://flic.kr/p/s6895e- time.jpg - https://flic.kr/p/4tNrxq- cheating.jpg - https://flic.kr/p/7FCr59- developing.jpg - https://flic.kr/p/bHLu96- change.jpg - https://flic.kr/p/6PtfXL- ownership.jpg - https://flic.kr/p/bwJSRV- pair_programming.jpg - https://flic.kr/p/QNdeB- unit_tests.jpg - https://flic.kr/p/7KEnN7- testing.jpg - https://flic.kr/p/tpCxq- test_driven.jpg - https://flic.kr/p/7Lx9Kk- refactoring.jpg - https://flic.kr/p/dUmmRN- context.jpg - https://flic.kr/p/93iAmM

Page 94: Min-Maxing Software Costs - Laracon EU 2015

Thank you!

Page 95: Min-Maxing Software Costs - Laracon EU 2015

Questions?Please, leave feedback: https://joind.in/15022