Recursive Development Teams

True North PHP

Scott Mattocks - November 3, 2012

@scottmattocks - scott@crisscott.com http://joind.in/7425

 

 

<?php
class Code {
    public function maintain() {
    	$this->fixBugs();
        if ($this->bugsExist()) {
            $this->maintain();
        }
    }
}
?>

Writing code is easy

<?php
$tweet  = 'Hello Twitter';
$result = post_tweet($tweet);

function post_tweet($tweet) {

  $conn = new OAuth(array('consumer_key' => '******',
    'consumer_secret' => '******', 'user_token' => '******',
    'user_secret' => '******'));

  $conn->request('POST', $conn->url('1/statuses/update'),
    array('status' => $tweet));

  return $conn->response['code'];
}
?>

This posts stuff to Twitter

Thanks, 140dev.com

Thanks, Dunechaser

Teams make it easy

Good teams...

  • write things down
  • know what to expect from each other
  • adapt to changes
  • don't let bad things bring them down
  • communicate clearly

Who's on your team?

Hint: if you didn't answer "my code" something is wrong

Is that all it does?

 

 

The more code you write to support the number crunchers, the less time you spend trying to figure out what the crunchers are doing

Good team members write things down

 

 

Good code logs things

A ⇒ B ⇒ C

 

Code is just a rough guideline for data to follow.

 

Data doesn't always listen. Like a shady salesman, it slips through cracks and changes its appearance on the fly.

This isn't logging

 

 

<?php
    try {
        $dbh = new PDO($dsn, $user, $password);
    } catch (PDOException $e) {
        die('Connection failed: ' . $e->getMessage());
    }
?>

I'm talking to you PDO::__construct() docs!

This is better

<?php
    try {
        log('Connecting to the database ' . $dsn);
        $dbh = new PDO($dsn, $user, $password);
    } catch (PDOException $e) {
        log('Connection failed to ' . $dsn . ': ' . $e->getMessage());
        exit;
    }
?>

Assuming the log function tracks some context for us.

This is pretty good

public function register($usernmae, $password) {
    $this->log->trace('Entering ' . __CLASS__ . '::' . __FUNCTION__ ' with username = ' . $username . ' and password = ' . str_repeat('*', strlen($password)));

    if (!$this->_validateUsername($username)) {
        $this->log->warn('Invalid username when trying to register a new user: ' . $username);
        $this->log->trace('Exiting ' . __CLASS__ . '::' . __FUNCTION__ . ' on error.');
        return;
    }

    /* Validate the password and do the registration */

    $this->log->info('New user registered with username ' . $username . ' and user ID ' . $this->_id);
    $this->log->trace('Exiting ' . __CLASS__ . '::' . __FUNCTION__ . ' on success.');
    return;
}

I am constrained to one slide. I might have added more logging. I certainly would have added some comments.

 

Sometimes you need a firehose

  • Trace - method by method, condition by condition.
  • Debug - If you put something in while writing the code, leave it there.
  • Info - Used to inform someone or something about a notable event.
  • Warn - Something unexpected happened, but we can recover.
  • Error - Something unexpected happened and we can't recover.

 

Good developers don't just tell you what they are doing now; they tell you what they are going to do.

 

 

Good code predicts the future using unit tests.

 

 

300 unit tests !== 1 application

Meh.

<?php
// Test set up stuff and mocking goes here.
$this->assertEquals($validator->validateEmail('bob@example.com'));
?>

 

 

Less Meh.

<?php
// Test set up stuff and mocking goes here.
$this->assertTrue($validator->validateEmail('scott.mattocks@gmail.com'));
?>

Good teams...

  • write things down (Logging)
  • know what to expect from each other (Unit Tests)

Good developers adapt quickly to changes.

 

 

Good code changes its behavior without changing the code.

 

 

Dev
QA
Staging
Produciton

 

 

$> svn ls $SVNROOT/awesomeproject/trunk
application/
bin/
conf/
doc/
lib/
tests/

 

 

My application is database driven. If we can't connect to the database, we have bigger problems.
— Me (1978 - 2011)
<?php
for (i = 0; $i < $config['db_primary_tries'], ++$i) {

  try {

    $db = new Database($config['db_host'], $config['db_user'], $config['db_password'])
    break;

  } catch (DBException $dbe) {

    $log->warn('Failed connecting to primary database ' . $config['db_host'] . ' ' . ($i + 1) . ' times. ' . $dbe->getMessage());
    usleep($config['db_retry_sleep']);

  }
}

if (empty($db)) {
  $log->error('Could not connect to primary databse ' . $config['db_host'] . ' after ' . $config['db_primary_tries'] . ' attempts. Trying secondary.');
}
// Rinse and repeat for the secondary.
?>

Good teams...

  • write things down (Logging)
  • know what to expect from each other (Unit Tests)
  • adapt quickly to changes (Configurations)

Good developers don't let bad developers bring them down.

Good code isolates features from each other.

 

 

Cache
Secondary DB
Primary DB

 

Expect failure, but don't accept it.

How to connect to a database or any other system

  1. Try to connect using the configured timeout settings
  2. Log failure as a warning
  3. Retry the configured number of times, waiting the configurable amount of times between each
  4. Try the configured back up
  5. Repeat 2 & 3 if unsuccessful, log success as a warning
  6. If no connections can be made successfully, log an error, but don't halt execution (it is up to the application to figure out if execution can continue)

 

You don't give someone production access the day you hire them.

 

Don't release features the instant you deploy the code.

 

If you take out the bad code, feature, thrid party, etc., you can relax a bit while fixing the problem.

Good teams...

  • write things down (Logging)
  • know what to expect from each other (Unit Tests)
  • adapt quickly to changes (Configurations)
  • don't let bad things bring them down (Isolation)

Good developers communicate well with non-technical folks.

Good code doesn't require you to read the code in order to know what the code is doing.

 

 

<?php
public function testWebDriver() {
    $home_page = new Home(self::$session);
    $home_page->open();
    $schedule_page = $home_page->navigate_to('Schedule');
    $schedule_page->open_session('WebDriver!');
    $this->assertEquals($schedule_page->title, "WebDriver!");
  }
?>
<?php
/**
 * Returns the array of AMQP arguments for the given queue.
 *
 * Depending on the configuration available, we may have one or more arguments which
 * need to be sent to RabbitMQ when the queue is declared. These arguments could be
 * things like high availability configurations.
 *
 * If something in getInstance() is failing, check here first. Trying to declare a
 * queue with a set of arguments that does not match the arguments which were used
 * the first time the queue was declared most likely will not work. Check the config
 * for AMQP and make sure that the arguments have not been changed since the queue
 * was originally created. The easiest way to reset them is to kill off the queue
 * and try to recreate it based on the new config.
 *
 * @param string $name The name of the queue.
 *
 * @return array The array of arguments from the config.
 */
?>
<?php
private static function _getQueueArgs($name)
{
    // Start with nothing.
    $args = array();

    // We may need to set some configuration arguments.
    $cfg = Settings\AMQP::getInstance();

    // Check for queue specific args first and then try defaults.
    if (array_key_exists($name, $cfg['queue_arguments'])) {
        $args = $cfg['queue_arguments'][$name];
    } elseif (array_key_exists('default', $cfg['queue_arguments'])) {
        $args = $cfg['queue_arguments']['default'];
    }

    return $args;
}
?>

Good teams...

 

  • write things down (Logging)
  • know what to expect from each other (Unit tests)
  • adapt quickly to changes (Configurations)
  • don't let bad things bring them down (Isolation)
  • communicate clearly (Documentation)

We're Hiring!

We're Everywhere

  • Waltham, MA
  • Washington, D.C.
  • San Francisco, CA
  • We sponsor visas

Great Benefits

  • Unlimited vacation and sick time
  • 401K matching
  • Hack day every 5 weeks

No Such Thing as Routine

  • DAUs measures in millions
  • Web, Mobile, and Social
  • PHP, Perl, Unity, Objective-C, Java, MongoDB…