Coding for Scale and Sanity 
Writing code you won’t regret later
Jim Keller 
Partner, Technology Director 
JimKellerES 
jimk@easternstandard.com 
http://easternstandard.com
Lamplighter 
2001-2009
The natural state of code 
is that it is constantly in flux
Code is split into core and crust
A misconception? 
Code that 
was written 
well 
Code that 
was written 
quickly
A note about 
Preferences 
( or: please don’t 
yell at me on 
Twitter )
Objects. Use them, even within procedural code. 
function _some_module_function() { 
try { 
require_once( dirname(__FILE__) . DIRECTORY_SEPARATOR . 
'my_class.php' ); 
$obj = new MyClass(); 
$obj->some_function(); 
} 
catch( Exception $e ) { 
watchdog( __FUNCTION__, $e->getMessage(), array(), 
WATCHDOG_ALERT ); 
return false; 
} 
}
Nomenclature 
beware of function definitions like: 
check_access( $role_id ) 
get_token() 
Not only are they (probably) too ambiguous, but they’re titled 
backwards. 
access_check( $role_id ) 
token_get() or token_generate()
<?php 
class MyAuthenticationClass { 
public function access_check_by_role_id( $role_id ); 
public function access_check_by_uid( $uid ); 
public function token_generate(); 
public function token_get_existing(); 
public function token_save(); 
} 
?>
<?php 
class MyAuthenticationClass { 
public $access_is_admin = false; 
public $access_force_override = false; 
public $token_serialize = false; 
public $token_check_cookie = true; 
public function access_check_by_role_id( $role_id ); 
public function access_check_by_uid( $uid ); 
public function token_generate(); 
public function token_get_existing(); 
public function token_save(); 
} 
?>
Regarding Exceptions… 
Use exceptions for error handling. Lots of them. 
Throw ‘em, catch ‘em, handle them. 
Don’t return “false” or “0” on error. 
public function count_jawns() { 
$count = some_other_func(); 
// zero might be a valid count... 
// so how will I know if this function 
// failed if some_other_function() returned false? 
return $count; 
}
Check your function & method arguments 
Check your arguments to make sure you got what you think 
you’ve got. 
If you’re expecting a uid, check to make sure it looks like a 
uid. 
This can help against security compromises (e.g. SQL 
injection), but more likely it’s just going to help you 
troubleshoot faster.
Check your arguments before you try to use them. 
public function access_check_by_uid( $uid, $args = array() ) { 
if ( !is_numeric($uid) ) { 
throw new InvalidArgumentException( 
'non-numeric UID found in ' . __METHOD__ ); 
} 
// stuff 
}
Don’t be afraid of utility functions 
public function access_check_by_uid( $uid, $args = array() ) { 
if ( !self::Uid_is_sane($uid) ) { 
throw new InvalidArgumentException( 
'non-numeric UID found in ' . __METHOD__ ); 
} 
// stuff 
} 
public static function Uid_is_sane( $uid ) { 
return is_numeric($uid); 
}
Arguing about arguments 
Your methods/functions should take as few arguments as 
possible. 
If you need many arguments, you either need to split into 
multiple functions, or pass an array of optional args. 
Don’t do this: 
public function access_check ( $uid = null, $entity_id, 
$user_obj = null, role_id =0, $force_override = false, 
$check_cookie = true );
Arguing about arguments 
All hope is lost when you see a method called like this: 
$this->access_check ( null, $id, $user, null, true, false );
Arguing about arguments 
public function access_check_by_uid( $uid, $entity_id, $args = 
array() ) { 
if ( !empty($args['force_override']) ) { 
//do something here 
} 
} 
public function access_check_by_role_id( $role_id, $entity_id, $args 
= array() ){ 
if ( !empty($args['force_override']) ) { 
//do something here 
} 
}
Arguing about arguments 
public function access_check_by_uid( $uid, $args = array() ) { 
$this->_Access_check( $uid, $args, self::ACCESS_PARAM_UID ); 
} 
public function access_check_by_role_id( $role_id, $args = array() ){ 
$this->_Access_check( $role_id, $args, self::ACCESS_PARAM_ROLE_ID ); 
} 
protected final function _Access_check( $id, $args, $type ) { 
if ( $type == self::ACCESS_PARAM_ROLE ) { 
// do some role stuff here 
} 
if ( $type == self::ACCESS_PARAM_UID ) { 
// do some uid-only stuff here 
} 
// shared stuff here 
}
I even have strong feelings about if statements 
If you find yourself writing long chains of if statements, 
considering writing a class factory instead. 
if ( $app_type == 'xbox' ) { 
$image_width = 100; 
$image_height = 120; 
} 
else if ( $app_type == 'android' ) { 
$image_width = 60; 
$image_height = 80; 
}
A more scalable approach 
interface ExternalAppOptionsManager() { 
public $image_height; 
public $image_width; 
} 
class OptionsManager_android implements ExternalAppOptionsManager(){ 
public $image_height = 80; 
public $image_width = 60; 
} 
class OptionsManager_xbox implements ExternalAppOptionsManager() { 
public $image_height = 120; 
public $image_width = 100; 
}
A quick & dirty class factory 
$options_class_name = 'OptionsManager_' . $app_type; 
if ( !require_once($options_class_name . '.php') || 
!class_exists($options_class_name) ) { 
throw new Exception('Invalid class: ' . $options_class_name); 
} 
$options_manager = new $options_class_name; 
$image_height = $options_manager->image_height; 
$image_width = $options_manager->image_width;
Conditional objects 
if ( $item_type == 't_shirt' ) { 
if ( $_SESSION['shipping_provider'] == 'USPS' ) { 
$base_price = 3.00; 
if ( $_SESSION['shipping_method'] == 'first_class' ) { 
include( 'usps_funcs.php' ); 
$real_rate = usps_get_rate( $_SESSION['shipping_method'], 
$item_weight ); 
$final_rate = $base_price + $real_rate; 
} 
} 
else if ( $_SESSION['shipping_provider'] == 'UPS' ) { 
$base_price = 3.00; 
if ( $_SESSION['shipping_method'] = '2_day' ) { 
include( 'ups_funcs.php' ); 
$real_rate = ups_get_rate( $_SESSION['shipping_method'], 
$item_weight ); 
} 
} 
// and so forth....
Conditional objects 
class ShippingConditional_usps extends ShippingConditional 
use USPS_API_Class; 
protected $_Shipping_method; 
protected $_Item; 
public function pricing_calculate() { 
$base_price = $this->base_price_by_shipping_method 
( $this->_Shipping_method ); 
$usps = new USPS_Api_Class; 
$usps->method_set( $this->shipping_method ); 
$real_price = $usps->rate_calculate( $item->weight ); 
return $real_price + $base_price; 
} 
}
Conditional objects 
if ( $item_type == 't_shirt' ) { 
if ( $_SESSION['shipping_provider'] == 'USPS' ) { 
$base_price = 3.00; 
if ( $_SESSION['shipping_method'] == 'first_class' ) { 
include( 'usps_funcs.php' ); 
$real_rate = usps_get_rate( $_SESSION['shipping_method'], 
$item_weight ); 
$final_rate = $base_price + $real_rate; 
} 
} 
else if ( $_SESSION['shipping_provider'] == 'UPS' ) { 
$base_price = 3.00; 
if ( $_SESSION['shipping_method'] = '2_day' ) { 
include( 'ups_funcs.php' ); 
$real_rate = ups_get_rate( $_SESSION['shipping_method'], 
$item_weight ); 
} 
} 
// and so forth....
A rewrite 
$shipping_class_name = "ShippingConditional_{$item_type}"; 
if ( !class_exists($shipping_class_name) ) { 
throw new InvalidArgumentException( "Invalid shipping class name: 
{$shipping_class_name}" ); 
} 
$shipping_obj = new $shipping_class_name; 
$shipping_obj->shipping_method_set($_SESSION['chosen_shipping_method']); 
$shipping_obj->item_set ( $cart_items[$j] ); 
$shipping_price = $shipping_obj->pricing_calculate();
In Closing 
- Assume that your code will have to change. Plan accordingly. 
- Learn to identify areas of code that are likely to get messy or 
change suddenly. Isolate these components in a way that it’s easy 
to work on them without having to refactor in many places. 
- For complex logic, don’t just write the logic in your code like a long 
narrative story. Break it out into classes and methods that process 
individual bits of the overall process.
Happy Coding. 
@JimKellerES 
jimk@easternstandard.com 
http://easternstandard.com

Coding for Scale and Sanity

  • 1.
    Coding for Scaleand Sanity Writing code you won’t regret later
  • 2.
    Jim Keller Partner,Technology Director JimKellerES jimk@easternstandard.com http://easternstandard.com
  • 4.
  • 8.
    The natural stateof code is that it is constantly in flux
  • 9.
    Code is splitinto core and crust
  • 10.
    A misconception? Codethat was written well Code that was written quickly
  • 11.
    A note about Preferences ( or: please don’t yell at me on Twitter )
  • 12.
    Objects. Use them,even within procedural code. function _some_module_function() { try { require_once( dirname(__FILE__) . DIRECTORY_SEPARATOR . 'my_class.php' ); $obj = new MyClass(); $obj->some_function(); } catch( Exception $e ) { watchdog( __FUNCTION__, $e->getMessage(), array(), WATCHDOG_ALERT ); return false; } }
  • 13.
    Nomenclature beware offunction definitions like: check_access( $role_id ) get_token() Not only are they (probably) too ambiguous, but they’re titled backwards. access_check( $role_id ) token_get() or token_generate()
  • 14.
    <?php class MyAuthenticationClass{ public function access_check_by_role_id( $role_id ); public function access_check_by_uid( $uid ); public function token_generate(); public function token_get_existing(); public function token_save(); } ?>
  • 15.
    <?php class MyAuthenticationClass{ public $access_is_admin = false; public $access_force_override = false; public $token_serialize = false; public $token_check_cookie = true; public function access_check_by_role_id( $role_id ); public function access_check_by_uid( $uid ); public function token_generate(); public function token_get_existing(); public function token_save(); } ?>
  • 16.
    Regarding Exceptions… Useexceptions for error handling. Lots of them. Throw ‘em, catch ‘em, handle them. Don’t return “false” or “0” on error. public function count_jawns() { $count = some_other_func(); // zero might be a valid count... // so how will I know if this function // failed if some_other_function() returned false? return $count; }
  • 17.
    Check your function& method arguments Check your arguments to make sure you got what you think you’ve got. If you’re expecting a uid, check to make sure it looks like a uid. This can help against security compromises (e.g. SQL injection), but more likely it’s just going to help you troubleshoot faster.
  • 18.
    Check your argumentsbefore you try to use them. public function access_check_by_uid( $uid, $args = array() ) { if ( !is_numeric($uid) ) { throw new InvalidArgumentException( 'non-numeric UID found in ' . __METHOD__ ); } // stuff }
  • 19.
    Don’t be afraidof utility functions public function access_check_by_uid( $uid, $args = array() ) { if ( !self::Uid_is_sane($uid) ) { throw new InvalidArgumentException( 'non-numeric UID found in ' . __METHOD__ ); } // stuff } public static function Uid_is_sane( $uid ) { return is_numeric($uid); }
  • 20.
    Arguing about arguments Your methods/functions should take as few arguments as possible. If you need many arguments, you either need to split into multiple functions, or pass an array of optional args. Don’t do this: public function access_check ( $uid = null, $entity_id, $user_obj = null, role_id =0, $force_override = false, $check_cookie = true );
  • 21.
    Arguing about arguments All hope is lost when you see a method called like this: $this->access_check ( null, $id, $user, null, true, false );
  • 22.
    Arguing about arguments public function access_check_by_uid( $uid, $entity_id, $args = array() ) { if ( !empty($args['force_override']) ) { //do something here } } public function access_check_by_role_id( $role_id, $entity_id, $args = array() ){ if ( !empty($args['force_override']) ) { //do something here } }
  • 23.
    Arguing about arguments public function access_check_by_uid( $uid, $args = array() ) { $this->_Access_check( $uid, $args, self::ACCESS_PARAM_UID ); } public function access_check_by_role_id( $role_id, $args = array() ){ $this->_Access_check( $role_id, $args, self::ACCESS_PARAM_ROLE_ID ); } protected final function _Access_check( $id, $args, $type ) { if ( $type == self::ACCESS_PARAM_ROLE ) { // do some role stuff here } if ( $type == self::ACCESS_PARAM_UID ) { // do some uid-only stuff here } // shared stuff here }
  • 24.
    I even havestrong feelings about if statements If you find yourself writing long chains of if statements, considering writing a class factory instead. if ( $app_type == 'xbox' ) { $image_width = 100; $image_height = 120; } else if ( $app_type == 'android' ) { $image_width = 60; $image_height = 80; }
  • 25.
    A more scalableapproach interface ExternalAppOptionsManager() { public $image_height; public $image_width; } class OptionsManager_android implements ExternalAppOptionsManager(){ public $image_height = 80; public $image_width = 60; } class OptionsManager_xbox implements ExternalAppOptionsManager() { public $image_height = 120; public $image_width = 100; }
  • 26.
    A quick &dirty class factory $options_class_name = 'OptionsManager_' . $app_type; if ( !require_once($options_class_name . '.php') || !class_exists($options_class_name) ) { throw new Exception('Invalid class: ' . $options_class_name); } $options_manager = new $options_class_name; $image_height = $options_manager->image_height; $image_width = $options_manager->image_width;
  • 27.
    Conditional objects if( $item_type == 't_shirt' ) { if ( $_SESSION['shipping_provider'] == 'USPS' ) { $base_price = 3.00; if ( $_SESSION['shipping_method'] == 'first_class' ) { include( 'usps_funcs.php' ); $real_rate = usps_get_rate( $_SESSION['shipping_method'], $item_weight ); $final_rate = $base_price + $real_rate; } } else if ( $_SESSION['shipping_provider'] == 'UPS' ) { $base_price = 3.00; if ( $_SESSION['shipping_method'] = '2_day' ) { include( 'ups_funcs.php' ); $real_rate = ups_get_rate( $_SESSION['shipping_method'], $item_weight ); } } // and so forth....
  • 28.
    Conditional objects classShippingConditional_usps extends ShippingConditional use USPS_API_Class; protected $_Shipping_method; protected $_Item; public function pricing_calculate() { $base_price = $this->base_price_by_shipping_method ( $this->_Shipping_method ); $usps = new USPS_Api_Class; $usps->method_set( $this->shipping_method ); $real_price = $usps->rate_calculate( $item->weight ); return $real_price + $base_price; } }
  • 29.
    Conditional objects if( $item_type == 't_shirt' ) { if ( $_SESSION['shipping_provider'] == 'USPS' ) { $base_price = 3.00; if ( $_SESSION['shipping_method'] == 'first_class' ) { include( 'usps_funcs.php' ); $real_rate = usps_get_rate( $_SESSION['shipping_method'], $item_weight ); $final_rate = $base_price + $real_rate; } } else if ( $_SESSION['shipping_provider'] == 'UPS' ) { $base_price = 3.00; if ( $_SESSION['shipping_method'] = '2_day' ) { include( 'ups_funcs.php' ); $real_rate = ups_get_rate( $_SESSION['shipping_method'], $item_weight ); } } // and so forth....
  • 30.
    A rewrite $shipping_class_name= "ShippingConditional_{$item_type}"; if ( !class_exists($shipping_class_name) ) { throw new InvalidArgumentException( "Invalid shipping class name: {$shipping_class_name}" ); } $shipping_obj = new $shipping_class_name; $shipping_obj->shipping_method_set($_SESSION['chosen_shipping_method']); $shipping_obj->item_set ( $cart_items[$j] ); $shipping_price = $shipping_obj->pricing_calculate();
  • 31.
    In Closing -Assume that your code will have to change. Plan accordingly. - Learn to identify areas of code that are likely to get messy or change suddenly. Isolate these components in a way that it’s easy to work on them without having to refactor in many places. - For complex logic, don’t just write the logic in your code like a long narrative story. Break it out into classes and methods that process individual bits of the overall process.
  • 32.
    Happy Coding. @JimKellerES jimk@easternstandard.com http://easternstandard.com