SlideShare a Scribd company logo
1 of 96
Download to read offline
MIN-MAXING
Software Costs
Konstantin K.
@everzet
TECHNICAL
DEBT
Without
frameworks
Simple
frameworks
Enterprise
frameworks
IS MESS
PART OF
A COURSE?
MIN-MAXING
SOFTWARE COSTS
SOFTWARE FORCES
• Creation - Introduction of a brand new feature
• Change - Business-driven modification of existing feature
• Ownership - Physical capability to change a feature
• Control - Capability to sustainably change a feature
SOFTWARE COSTS
1. Cost of Creation
2. Cost of Change
3. Cost of Control
COST OF CREATION
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation
CONVENTIONS
OPTIMISE
FOR CREATION
$ bin/rails generate controller welcome index
from django.contrib import admin
from . import models
admin.site.register(models.Article)
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
...
}
CREATION
IS LIMITED BY
THE LIFE SPAN
Creation
→
Change
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation
Creation
→
Change
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation
Pure observation &
personal experience
Creation
→
Change
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation
Conventional Project
Creation
→
Change
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation
Conventional Project
Conventional Project
Convention-based projects either
die a hero or live long enough to
see themselves become the villain.
COST OF CHANGE
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation Cost of Change
THE SEARCH
FUNCTION
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);
$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');
}
HOW LONG WOULD
IT TAKETO ADD TAGS
SUPPORT?
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');
}
{
$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');
{
$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');
{
$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');
CHANGE
KICKS IN AFTER
CREATION STOPS
Creation
→
Change
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation Cost of Change
Creation
→
Change
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation Cost of Change
Enterprise Project
Creation
→
Change
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation Cost of Change
Enterprise Project
Enterprise Project
FORCE DYNAMICS
CONVENTION-BASED
FRAMEWORK
Your first feature
Controlled Code
Controlled Code
Your second feature
Controlled Code
Controlled Code
Controlled Code
Delegated Code
The framework / library code
Controlled Code
Delegated Code Controlled Code
Delegated Code
Change
Controlled Code
Delegated Code
Change
Controlled Code
Delegated Code
Change
Controlled Code
Owned Code Delegated Code
Change
Controlled Code
Owned Code Delegated Code
Change
Controlled Code
Owned Code Delegated Code
Change
Controlled Code
Owned Code Delegated Code Controlled Code
Owned Code Controlled Code
– An engineer
“Ah, to hell with that!”
Owned Code
Owned Code
Change
Owned Code
ENTERPRISE
FRAMEWORK
Controlled Code
Your first feature
Controlled Code
Controlled Code
Your second feature
Controlled Code
Controlled Code
Change
Controlled Code
Change
Controlled Code
Change
– A business person
“Why does it always take so long?”
Controlled CodeOwned Code
Change
Controlled CodeOwned Code
Change
Controlled CodeOwned Code
Change
Controlled CodeOwned Code
Owned Code
CONTROL HAS COST
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation Cost of Change Cost of Control
CONTROL
IS A LIMITER FOR A
COST OF CHANGE
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation Cost of Change Cost of Control
CONTROLLING
TOO FEW
TOO LATE
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation Cost of Change Cost of Control
CONTROLLING
TOO MUCH
TOO EARLY
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation Cost of Change Cost of Control
OWNING MORE
THANYOU CONTROL
IS UNSUSTAINABLE
CONTROLLING
EVERYTHING
IS EXPENSIVE
MIN-MAXING
SOFTWARE COSTS
4 RULES OF MIN-MAXING
1. Begin from owning nothing ( )
2. Take ownership reluctantly ( )
3. Control everything you own ( )
4. Continuously reassess your control ( )
HEALTHY YOUNG PROJECT
Owned Code Delegated Code Controlled Code
UNHEALTHY YOUNG PROJECT
Owned Code Delegated Code Controlled Code
UNHEALTHY YOUNG PROJECT
Owned Code Delegated Code Controlled Code
HEALTHY MATURE PROJECT
Owned Code Delegated Code Controlled Code
UNHEALTHY “MATURE” PROJECT
Owned Code Delegated Code Controlled Code
UNHEALTHY “MATURE” PROJECT
Owned Code Delegated Code Controlled Code
1. BEGIN
FROM OWNING
NOTHING
2.TAKE
OWNERSHIP
RELUCTANTLY
3. CONTROL
EVERYTHING
YOU OWN
4. CONTINUOUSLY
REASSESSYOUR
CONTROL
Project Lifetime
Beginning 3 months 6 months 9 months ...
Cost of Creation Cost of Change Cost of Control
Control everything you write.
Avoid writing anything.
Deliver business impact,
not software.
THANK YOU
FORYOURTIME!
Konstantin K.
@everzet

More Related Content

What's hot

Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDAleix Vergés
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleHugo Hamon
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteLeonardo Proietti
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & RESTHugo Hamon
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsRoss Tuck
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationKirill Chebunin
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolveXSolve
 
Command Bus To Awesome Town
Command Bus To Awesome TownCommand Bus To Awesome Town
Command Bus To Awesome TownRoss Tuck
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web servicesMichelangelo van Dam
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Kacper Gunia
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersKacper Gunia
 
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
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in actionJace Ju
 
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowForget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowKacper Gunia
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsSam Hennessy
 
November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2Kacper Gunia
 

What's hot (20)

Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and Hobgoblins
 
Mocking Demystified
Mocking DemystifiedMocking Demystified
Mocking Demystified
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 Application
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolve
 
Command Bus To Awesome Town
Command Bus To Awesome TownCommand Bus To Awesome Town
Command Bus To Awesome Town
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4Developers
 
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
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in action
 
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowForget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers Cracow
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2
 

Viewers also liked

Viewers also liked (12)

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
 
Modern Agile Project Toolbox
Modern Agile Project ToolboxModern Agile Project Toolbox
Modern Agile Project Toolbox
 
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
 
BDD by example
BDD by exampleBDD by example
BDD by example
 
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
 
Modern Project Toolbox
Modern Project ToolboxModern Project Toolbox
Modern Project Toolbox
 
Being effective with legacy projects
Being effective with legacy projectsBeing effective with legacy projects
Being effective with legacy projects
 
BDD в PHP с Behat и Mink
BDD в PHP с Behat и MinkBDD в PHP с Behat и Mink
BDD в PHP с Behat и Mink
 
BDD для PHP проектов
BDD для PHP проектовBDD для PHP проектов
BDD для PHP проектов
 
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 with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDD
 

Similar to Min-Maxing Software Costs

Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011Alessandro Nadalin
 
PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2eugenio pombi
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejsNick Lee
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From IusethisMarcus Ramberg
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan Subedi
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说Ting Lv
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
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
 
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
 
Things I Believe Now That I'm Old
Things I Believe Now That I'm OldThings I Believe Now That I'm Old
Things I Believe Now That I'm OldRoss Tuck
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your CodeAbbas Ali
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using CodeceptionJeroen van Dijk
 
(PHPers Wrocław #5) How to write valuable unit test?
(PHPers Wrocław #5) How to write valuable unit test?(PHPers Wrocław #5) How to write valuable unit test?
(PHPers Wrocław #5) How to write valuable unit test?RST Software Masters
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Fabien Potencier
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxMichelangelo van Dam
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutesBarang CK
 

Similar to Min-Maxing Software Costs (20)

Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 
Bacbkone js
Bacbkone jsBacbkone js
Bacbkone js
 
PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From Iusethis
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
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
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework apps
 
Things I Believe Now That I'm Old
Things I Believe Now That I'm OldThings I Believe Now That I'm Old
Things I Believe Now That I'm Old
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your Code
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using Codeception
 
(PHPers Wrocław #5) How to write valuable unit test?
(PHPers Wrocław #5) How to write valuable unit test?(PHPers Wrocław #5) How to write valuable unit test?
(PHPers Wrocław #5) How to write valuable unit test?
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
 

Recently uploaded

Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 

Recently uploaded (20)

Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 

Min-Maxing Software Costs

  • 6. SOFTWARE FORCES • Creation - Introduction of a brand new feature • Change - Business-driven modification of existing feature • Ownership - Physical capability to change a feature • Control - Capability to sustainably change a feature
  • 7. SOFTWARE COSTS 1. Cost of Creation 2. Cost of Change 3. Cost of Control
  • 9. Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation
  • 11. $ bin/rails generate controller welcome index
  • 12. from django.contrib import admin from . import models admin.site.register(models.Article)
  • 13. package hello; import java.util.concurrent.atomic.AtomicLong; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class GreetingController { ... }
  • 15. Creation → Change Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation
  • 16. Creation → Change Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Pure observation & personal experience
  • 17. Creation → Change Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Conventional Project
  • 18. Creation → Change Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Conventional Project Conventional Project
  • 19. Convention-based projects either die a hero or live long enough to see themselves become the villain.
  • 21. Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Cost of Change
  • 23. 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'); }
  • 24. 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'); }
  • 25. HOW LONG WOULD IT TAKETO ADD TAGS SUPPORT?
  • 26. 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'); }
  • 27. 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'); }
  • 28. { $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');
  • 29. { $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'); { $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');
  • 31. Creation → Change Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Cost of Change
  • 32. Creation → Change Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Cost of Change Enterprise Project
  • 33. Creation → Change Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Cost of Change Enterprise Project Enterprise Project
  • 36.
  • 42. Delegated Code The framework / library code Controlled Code
  • 47. Owned Code Delegated Code Change Controlled Code
  • 48. Owned Code Delegated Code Change Controlled Code
  • 49. Owned Code Delegated Code Change Controlled Code
  • 50. Owned Code Delegated Code Controlled Code
  • 52. – An engineer “Ah, to hell with that!”
  • 57.
  • 65. – A business person “Why does it always take so long?”
  • 72. Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Cost of Change Cost of Control
  • 73. CONTROL IS A LIMITER FOR A COST OF CHANGE
  • 74. Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Cost of Change Cost of Control
  • 76. Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Cost of Change Cost of Control
  • 78. Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Cost of Change Cost of Control
  • 82. 4 RULES OF MIN-MAXING 1. Begin from owning nothing ( ) 2. Take ownership reluctantly ( ) 3. Control everything you own ( ) 4. Continuously reassess your control ( )
  • 83. HEALTHY YOUNG PROJECT Owned Code Delegated Code Controlled Code
  • 84. UNHEALTHY YOUNG PROJECT Owned Code Delegated Code Controlled Code
  • 85. UNHEALTHY YOUNG PROJECT Owned Code Delegated Code Controlled Code
  • 86. HEALTHY MATURE PROJECT Owned Code Delegated Code Controlled Code
  • 87. UNHEALTHY “MATURE” PROJECT Owned Code Delegated Code Controlled Code
  • 88. UNHEALTHY “MATURE” PROJECT Owned Code Delegated Code Controlled Code
  • 93. Project Lifetime Beginning 3 months 6 months 9 months ... Cost of Creation Cost of Change Cost of Control
  • 94. Control everything you write. Avoid writing anything.