SlideShare a Scribd company logo
1 of 95
Download to read offline
Min-Maxing
Software Costs
@everzet
@inviqa
What is this talk about?
Harm that "Laravel Facades"
inflict on not-suspecting
developers.
Bad idea that is Active Record
and Eloquent.
Other framework's superiority
over Laravel.
Killing kittens.
And other subjective and
nonconstructive crap like that ...
... is not in this talk.
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
Context,
the talk
Software Costs
Software Costs
Software Costs
1. Time to write & test code
2. Time to change code & tests
3. Time to refactor code & tests
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
Cost of
Introduction
Cost of Introduction
Time it takes to introduce new,
naturally independent
application logic.
Attributes
— Has direct correlation to
business value
— Has direct correlation to LOC
— Relatively easy to optimise by
generalisation
Dynamics
— Visible from the outset
— Loses relevancy over the
project lifetime
— Stable across projects
Dynamics
— Visible from the outset
— Loses relevancy over the
project lifetime
— Stable across projects
Cost of Introduction is relatively
easy to optimise.
Optimising for CoI:
Convenience Layer
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');
// ...
}
Base Class
// --- Base Controller
class MyController extends Controller
{
public function indexAction()
{
$homepageUrl = $this->generateUrl('homepage');
// ...
}
}
Optimising for CoI:
Generalisation
Active Record
// --- Custom Mapping
class DbalCustomerRepository implements CustomerRepository
{
public function findCustomerWithName($name) {
// ...
}
}
// --- Eloquent
use IlluminateDatabaseEloquentModel;
class Customer extends Model
{
// ...
}
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));
Dependency Injection Container
// --- Dependency Inversion Principle
$contoller = new MyController(
new Router(),
new Cache(new Configuration())
);
// --- Dependency Injection Container
$controller = $container->get('controller.my');
No matter what you think,
optimising for CoI (Cost of
Introduction) is not inherently a
bad thing.
A Cut-Off of the product
If the product life is short enough
to not encounter loss of CoI
relevancy, then the CoI is the
only cost worth optimising for.
Convenience based projects
either die a hero or live long
enough to see themselves
become the villain.
Cost of
Change
Cost of Change
Time it takes to adapt the
existing application logic to new
business realities.
Attributes
— Has direct correlation to
business value
— Has no direct correlation to
LOC
— Affected by generalisation
Dynamics
— Invisible from the outset
— Gains relevancy during the
project lifetime
— Exponentially increases over
time
Cost of Change increases
exponentially over time.
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');
}
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');
}
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');
}
Exponential increase of Cost of
Change is not inherently a
problem of every product.
A Cut-Off of the product
If the product life is long enough
to encounter exponential growth
of CoC, then the CoC is the cost
worth optimising for.
If you want to change the world
with your product, then the
change is the primary thing your
product must prepare for.
Optimising for Cost of
Introduction in most cases has a
negative effect on the Cost of
Change curve.
That's why some engineers try to
increase the Cost of Introduction
in attempt to affect the Cost of
Change curve.
Cost of Introduction and Change
Increasing Cost of Introduction
Cost of Change with increased Cost of Introduction
Upfront Design (aka Waterfall)
Illusion that one can control
cost of change by applying
enough analysis upfront.
Upfront Design fails to achieve
long-lasting effect because both
rate and nature of change for
arbitrary domain is
unpredictable.
Cost of
Ownership
Cost of Ownership
Time it takes to maintain the
owned application logic to
support its ongoing change.
Attributes
— Intermediate between Cost of
Introduction & Cost of
Change
— Has no direct correlation to
business value
— Has direct correlation to LOC
Dynamics
— Always invisible
— Always relevant
— Stable over time, but adds up
Cost of Ownership is the cost you
pay for the right to change a
particular part (module, class,
method) of application
continuosly and sustainably.
Testing
Unit testing
Refactoring
Introducing Cost of Ownership
allows you to balance two other
costs.
Cost of Introduction and Change
Cost of Introduction and Ownership
Cost of Ownership effect on Cost of Change curve
Emergent Design
Usual result of ongoing
ownership.
Cost of Ownership of everything
Cost of Ownership of everything
Owning everything fails to
achieve ever-increasing benefits,
because you rarely need to
change the entire system.
End-to-end testing
is owning everything
Cost of Ownership of the wrong thing
Ownership wouldn't help if
you're owning the wrong thing.
Exclusive end-to-end testing
is owning the wrong thing
You do want to own everything
worth owning.
But you don't know what's worth
owning at the beginning of the
project.
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.
Gaming
Software Costs
Own only the logic you need to
change.
Write only the logic you need to
own.
Own everything you write.
Own everything you write.
Try to not write anything.
Own everything you write.
Try to not write anything.
Reuse everything else.
1. Document the need
2. Spike - Experiment with
tools available
3. Document changes &
constraints
4. Stabilise - Claim
ownership when the thing
grows outside of tool
boundaries
5. Isolate Religiously
Steps
1. Document the need
2. Spike
3. Document changes & constraints
4. Stabilise
5. Isolate Religiously
Unit testing
is owning
Refactoring
is owning
Test Driven Development
is an ownership technique
Gaming Software Costs
1. Document
2. Spike & Stabilise
3. Use TDD for stabilisation
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
Thank you!
Questions?Please, leave feedback: https://joind.in/15022

More Related Content

What's hot

Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Colin O'Dell
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationSamuel ROZE
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsŁukasz Chruściel
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan Subedi
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patternsSamuel ROZE
 
Workshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptWorkshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptVisual Engineering
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingSamuel ROZE
 
PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2eugenio pombi
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form componentSamuel ROZE
 
Backbone js
Backbone jsBackbone js
Backbone jsrstankov
 
05 JavaScript #burningkeyboards
05 JavaScript #burningkeyboards05 JavaScript #burningkeyboards
05 JavaScript #burningkeyboardsDenis Ristic
 
Symfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsIgnacio Martín
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsMichelangelo van Dam
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11Michelangelo van Dam
 
When cqrs meets event sourcing
When cqrs meets event sourcingWhen cqrs meets event sourcing
When cqrs meets event sourcingManel Sellés
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Railsrstankov
 

What's hot (20)

Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony application
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patterns
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
Workshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptWorkshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScript
 
Fatc
FatcFatc
Fatc
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event Sourcing
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
 
Backbone js
Backbone jsBackbone js
Backbone js
 
05 JavaScript #burningkeyboards
05 JavaScript #burningkeyboards05 JavaScript #burningkeyboards
05 JavaScript #burningkeyboards
 
Symfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worlds
 
PHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the testsPHPUnit Episode iv.iii: Return of the tests
PHPUnit Episode iv.iii: Return of the tests
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
Why ruby
Why rubyWhy ruby
Why ruby
 
When cqrs meets event sourcing
When cqrs meets event sourcingWhen cqrs meets event sourcing
When cqrs meets event sourcing
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 

Similar to Min-Maxing Software Costs - Laracon EU 2015

Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosDivante
 
Paying off technical debt with PHPSpec
Paying off technical debt with PHPSpecPaying off technical debt with PHPSpec
Paying off technical debt with PHPSpecLewis Wright
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needKacper Gunia
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenerytoddbr
 
Load Testing with PHP and RedLine13
Load Testing with PHP and RedLine13Load Testing with PHP and RedLine13
Load Testing with PHP and RedLine13Jason Lotito
 
Crossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkCrossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkDaniel Spector
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
Practical AngularJS
Practical AngularJSPractical AngularJS
Practical AngularJSWei Ru
 
"Angular.js Concepts in Depth" by Aleksandar Simović
"Angular.js Concepts in Depth" by Aleksandar Simović"Angular.js Concepts in Depth" by Aleksandar Simović
"Angular.js Concepts in Depth" by Aleksandar SimovićJS Belgrade
 
Automating Performance Monitoring at Microsoft
Automating Performance Monitoring at MicrosoftAutomating Performance Monitoring at Microsoft
Automating Performance Monitoring at MicrosoftThousandEyes
 
Building Testable PHP Applications
Building Testable PHP ApplicationsBuilding Testable PHP Applications
Building Testable PHP Applicationschartjes
 
Measuring Your Code
Measuring Your CodeMeasuring Your Code
Measuring Your CodeNate Abele
 
Measuring Your Code 2.0
Measuring Your Code 2.0Measuring Your Code 2.0
Measuring Your Code 2.0Nate Abele
 
PHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better CodePHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better CodeSWIFTotter Solutions
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Codeerikmsp
 
Groovy on Grails by Ziya Askerov
Groovy on Grails by Ziya AskerovGroovy on Grails by Ziya Askerov
Groovy on Grails by Ziya AskerovVuqar Suleymanov
 

Similar to Min-Maxing Software Costs - Laracon EU 2015 (20)

Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenarios
 
Paying off technical debt with PHPSpec
Paying off technical debt with PHPSpecPaying off technical debt with PHPSpec
Paying off technical debt with PHPSpec
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
PHP MVC
PHP MVCPHP MVC
PHP MVC
 
Load Testing with PHP and RedLine13
Load Testing with PHP and RedLine13Load Testing with PHP and RedLine13
Load Testing with PHP and RedLine13
 
Crossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkCrossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end Framework
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Practical AngularJS
Practical AngularJSPractical AngularJS
Practical AngularJS
 
"Angular.js Concepts in Depth" by Aleksandar Simović
"Angular.js Concepts in Depth" by Aleksandar Simović"Angular.js Concepts in Depth" by Aleksandar Simović
"Angular.js Concepts in Depth" by Aleksandar Simović
 
Automating Performance Monitoring at Microsoft
Automating Performance Monitoring at MicrosoftAutomating Performance Monitoring at Microsoft
Automating Performance Monitoring at Microsoft
 
Building Testable PHP Applications
Building Testable PHP ApplicationsBuilding Testable PHP Applications
Building Testable PHP Applications
 
99% is not enough
99% is not enough99% is not enough
99% is not enough
 
Measuring Your Code
Measuring Your CodeMeasuring Your Code
Measuring Your Code
 
Measuring Your Code 2.0
Measuring Your Code 2.0Measuring Your Code 2.0
Measuring Your Code 2.0
 
PHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better CodePHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better Code
 
Working Effectively With Legacy Perl Code
Working Effectively With Legacy Perl CodeWorking Effectively With Legacy Perl Code
Working Effectively With Legacy Perl Code
 
Grails
GrailsGrails
Grails
 
Grails
GrailsGrails
Grails
 
Groovy on Grails by Ziya Askerov
Groovy on Grails by Ziya AskerovGroovy on Grails by Ziya Askerov
Groovy on Grails by Ziya Askerov
 

More from Konstantin Kudryashov

Being effective with legacy projects
Being effective with legacy projectsBeing effective with legacy projects
Being effective with legacy projectsKonstantin Kudryashov
 
Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast Konstantin Kudryashov
 
Moving away from legacy code (AgileCymru)
Moving away from legacy code  (AgileCymru)Moving away from legacy code  (AgileCymru)
Moving away from legacy code (AgileCymru)Konstantin Kudryashov
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDDKonstantin Kudryashov
 
Enabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsEnabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsKonstantin Kudryashov
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDDKonstantin Kudryashov
 
LESS, SASS, HAML: 4 буквы, изменившие frontend development
LESS, SASS, HAML: 4 буквы, изменившие frontend developmentLESS, SASS, HAML: 4 буквы, изменившие frontend development
LESS, SASS, HAML: 4 буквы, изменившие frontend developmentKonstantin Kudryashov
 
Автоматизируем деплоймент проекта с помощью Capistrano
Автоматизируем деплоймент проекта с помощью CapistranoАвтоматизируем деплоймент проекта с помощью Capistrano
Автоматизируем деплоймент проекта с помощью CapistranoKonstantin Kudryashov
 

More from Konstantin Kudryashov (15)

Modern Agile Project Toolbox
Modern Agile Project ToolboxModern Agile Project Toolbox
Modern Agile Project Toolbox
 
Being effective with legacy projects
Being effective with legacy projectsBeing effective with legacy projects
Being effective with legacy projects
 
Modern Project Toolbox
Modern Project ToolboxModern Project Toolbox
Modern Project Toolbox
 
Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast
 
Moving away from legacy code (AgileCymru)
Moving away from legacy code  (AgileCymru)Moving away from legacy code  (AgileCymru)
Moving away from legacy code (AgileCymru)
 
Taking back BDD
Taking back BDDTaking back BDD
Taking back BDD
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDD
 
Enabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsEnabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projects
 
Behat 3.0 meetup (March)
Behat 3.0 meetup (March)Behat 3.0 meetup (March)
Behat 3.0 meetup (March)
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDD
 
BDD в PHP с Behat и Mink
BDD в PHP с Behat и MinkBDD в PHP с Behat и Mink
BDD в PHP с Behat и Mink
 
BDD in Symfony2
BDD in Symfony2BDD in Symfony2
BDD in Symfony2
 
BDD для PHP проектов
BDD для PHP проектовBDD для PHP проектов
BDD для PHP проектов
 
LESS, SASS, HAML: 4 буквы, изменившие frontend development
LESS, SASS, HAML: 4 буквы, изменившие frontend developmentLESS, SASS, HAML: 4 буквы, изменившие frontend development
LESS, SASS, HAML: 4 буквы, изменившие frontend development
 
Автоматизируем деплоймент проекта с помощью Capistrano
Автоматизируем деплоймент проекта с помощью CapistranoАвтоматизируем деплоймент проекта с помощью Capistrano
Автоматизируем деплоймент проекта с помощью Capistrano
 

Recently uploaded

DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clashcharlottematthew16
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 

Recently uploaded (20)

DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clash
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 

Min-Maxing Software Costs - Laracon EU 2015

  • 4. What is this talk about?
  • 5. Harm that "Laravel Facades" inflict on not-suspecting developers.
  • 6. Bad idea that is Active Record and Eloquent.
  • 9. And other subjective and nonconstructive crap like that ...
  • 10. ... is not in this talk.
  • 11. 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
  • 15. Software Costs 1. Time to write & test code 2. Time to change code & tests 3. Time to refactor code & tests
  • 16. 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
  • 18. Cost of Introduction Time it takes to introduce new, naturally independent application logic.
  • 19. Attributes — Has direct correlation to business value — Has direct correlation to LOC — Relatively easy to optimise by generalisation
  • 20. Dynamics — Visible from the outset — Loses relevancy over the project lifetime — Stable across projects
  • 21. Dynamics — Visible from the outset — Loses relevancy over the project lifetime — Stable across projects
  • 22. Cost of Introduction is relatively easy to optimise.
  • 24. 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'); // ... }
  • 25. Base Class // --- Base Controller class MyController extends Controller { public function indexAction() { $homepageUrl = $this->generateUrl('homepage'); // ... } }
  • 27. Active Record // --- Custom Mapping class DbalCustomerRepository implements CustomerRepository { public function findCustomerWithName($name) { // ... } } // --- Eloquent use IlluminateDatabaseEloquentModel; class Customer extends Model { // ... }
  • 28. 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));
  • 29. Dependency Injection Container // --- Dependency Inversion Principle $contoller = new MyController( new Router(), new Cache(new Configuration()) ); // --- Dependency Injection Container $controller = $container->get('controller.my');
  • 30. No matter what you think, optimising for CoI (Cost of Introduction) is not inherently a bad thing.
  • 31. A Cut-Off of the product
  • 32. If the product life is short enough to not encounter loss of CoI relevancy, then the CoI is the only cost worth optimising for.
  • 33. Convenience based projects either die a hero or live long enough to see themselves become the villain.
  • 35. Cost of Change Time it takes to adapt the existing application logic to new business realities.
  • 36. Attributes — Has direct correlation to business value — Has no direct correlation to LOC — Affected by generalisation
  • 37. Dynamics — Invisible from the outset — Gains relevancy during the project lifetime — Exponentially increases over time
  • 38. Cost of Change increases exponentially over time.
  • 39. 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'); }
  • 40. 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'); }
  • 41. 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'); }
  • 42. Exponential increase of Cost of Change is not inherently a problem of every product.
  • 43. A Cut-Off of the product
  • 44. If the product life is long enough to encounter exponential growth of CoC, then the CoC is the cost worth optimising for.
  • 45. If you want to change the world with your product, then the change is the primary thing your product must prepare for.
  • 46. Optimising for Cost of Introduction in most cases has a negative effect on the Cost of Change curve.
  • 47. That's why some engineers try to increase the Cost of Introduction in attempt to affect the Cost of Change curve.
  • 48. Cost of Introduction and Change
  • 49. Increasing Cost of Introduction
  • 50. Cost of Change with increased Cost of Introduction
  • 51. Upfront Design (aka Waterfall) Illusion that one can control cost of change by applying enough analysis upfront.
  • 52. Upfront Design fails to achieve long-lasting effect because both rate and nature of change for arbitrary domain is unpredictable.
  • 54. Cost of Ownership Time it takes to maintain the owned application logic to support its ongoing change.
  • 55. Attributes — Intermediate between Cost of Introduction & Cost of Change — Has no direct correlation to business value — Has direct correlation to LOC
  • 56. Dynamics — Always invisible — Always relevant — Stable over time, but adds up
  • 57. Cost of Ownership is the cost you pay for the right to change a particular part (module, class, method) of application continuosly and sustainably.
  • 61. Introducing Cost of Ownership allows you to balance two other costs.
  • 62. Cost of Introduction and Change
  • 63. Cost of Introduction and Ownership
  • 64. Cost of Ownership effect on Cost of Change curve
  • 65. Emergent Design Usual result of ongoing ownership.
  • 66. Cost of Ownership of everything
  • 67. Cost of Ownership of everything
  • 68. Owning everything fails to achieve ever-increasing benefits, because you rarely need to change the entire system.
  • 70. Cost of Ownership of the wrong thing
  • 71. Ownership wouldn't help if you're owning the wrong thing.
  • 72. Exclusive end-to-end testing is owning the wrong thing
  • 73. You do want to own everything worth owning.
  • 74. But you don't know what's worth owning at the beginning of the project.
  • 75. 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.
  • 77. Own only the logic you need to change.
  • 78. Write only the logic you need to own.
  • 80. Own everything you write. Try to not write anything.
  • 81. Own everything you write. Try to not write anything. Reuse everything else.
  • 83. 2. Spike - Experiment with tools available
  • 84. 3. Document changes & constraints
  • 85. 4. Stabilise - Claim ownership when the thing grows outside of tool boundaries
  • 87. Steps 1. Document the need 2. Spike 3. Document changes & constraints 4. Stabilise 5. Isolate Religiously
  • 90. Test Driven Development is an ownership technique
  • 91.
  • 92. Gaming Software Costs 1. Document 2. Spike & Stabilise 3. Use TDD for stabilisation
  • 93. 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
  • 95. Questions?Please, leave feedback: https://joind.in/15022