coding for scale and sanity

Download Coding for Scale and Sanity

Post on 24-Jun-2015




0 download

Embed Size (px)


Presentation for Drupaldelphia 2014. Given by Jim Keller of EasternStandard ( Description: No developer in history had enough time and enough up-front information to make perfectly scalable architecture decisions, get everything right the first time, and craft all of their code exquisitely right out of the gate. Coding is an organic process, and often one that's driven by changing requirements, dreadful deadlines, and unreliable third parties. It's a fact of our lives: you will inevitably end up writing code you're not proud of because you needed to get something done in a pinch. That said, the tradeoff between speed, flexibility, and quality doesn't have to be as drastic as you might think. In this session, I will share a few methodologies and tricks for writing quick, flexible code that doesn't lock you into technical debt and doesn't require you to sacrifice your dignity as a software developer. Also included are some general tips and techniques for writing scalable code that will help future-you not hate current-you for some of the decisions you've been making.


  • 1. Coding for Scale and SanityWriting code you wont regret later

2. Jim KellerPartner, Technology DirectorJimKellerESjimk@easternstandard.com 3. Lamplighter2001-2009 4. The natural state of codeis that it is constantly in flux 5. Code is split into core and crust 6. A misconception?Code thatwas writtenwellCode thatwas writtenquickly 7. A note aboutPreferences( or: please dontyell at me onTwitter ) 8. 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;}} 9. Nomenclaturebeware of function definitions like:check_access( $role_id )get_token()Not only are they (probably) too ambiguous, but theyre titledbackwards.access_check( $role_id )token_get() or token_generate() 10. 11. 12. Regarding ExceptionsUse exceptions for error handling. Lots of them.Throw em, catch em, handle them.Dont 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;} 13. Check your function & method argumentsCheck your arguments to make sure you got what you thinkyouve got.If youre expecting a uid, check to make sure it looks like auid.This can help against security compromises (e.g. SQLinjection), but more likely its just going to help youtroubleshoot faster. 14. 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} 15. Dont be afraid of utility functionspublic 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);} 16. Arguing about argumentsYour methods/functions should take as few arguments aspossible.If you need many arguments, you either need to split intomultiple functions, or pass an array of optional args.Dont do this:public function access_check ( $uid = null, $entity_id,$user_obj = null, role_id =0, $force_override = false,$check_cookie = true ); 17. Arguing about argumentsAll hope is lost when you see a method called like this:$this->access_check ( null, $id, $user, null, true, false ); 18. Arguing about argumentspublic 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}} 19. Arguing about argumentspublic 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} 20. I even have strong feelings about if statementsIf 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;} 21. A more scalable approachinterface 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;} 22. 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; 23. Conditional objectsif ( $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.... 24. Conditional objectsclass ShippingConditional_usps extends ShippingConditionaluse 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;}} 25. Conditional objectsif ( $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.... 26. 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(); 27. In Closing- Assume that your code will have to change. Plan accordingly.- Learn to identify areas of code that are likely to get messy orchange suddenly. Isolate these components in a way that its easyto work on them without having to refactor in many places.- For complex logic, dont just write the logic in your code like a longnarrative story. Break it out into classes and methods that processindividual bits of the overall process. 28. Happy Coding.@JimKellerESjimk@easternstandard.com