On Changing Jobs

An apprentice recently sent me an e-mail:

For the past month or two my work has been a not fun and I’ve been very unhappy. I felt like I was being mismanaged, being setup for failure, thrown under the bus for things that were out of my control, and the lack of on-boarding really hurt my ability.

Two friends have asked to join their companies and I have interviewed but have not completed them. The company I am at is pretty good and it would be a positive step in my career but I’m quite miserable. I was working 14 hrs/day for a few days out of the week and a min of 9/10 hrs. It just caught up to me.

Then my boss quite and now I’m being managed by people in [redacted]. They discovered my work hours and cut it back to a normal day, sprints push back when feature creep happen, but I still have that bad taste in my mouth. Things have gotten a little bit better but the idea of working with my old boss from a previous company is tempting. Plus the code I’d be writing would be relevant and not just for profit.

Do you have any advice?

He later told me my response to his question was blog-worthy, so I thought I’d publish it for posterity.

Here are my observations:

1) Burnout is a very real thing. If you’re doing 50-60 hour weeks, that can be a fairly good sign of mismanagement. It’s good that you aren’t doing this anymore, but obviously that alone isn’t going to determine whether or not you’re happy.

2) In terms of money, most people ultimately work for someone else’s gain. You’ll be hard-pressed to find a job where you aren’t the source of another person’s dollar. In terms of having an impact on the world around you, whether or not you’re making the impact you want to make is something only you can decide for yourself. That said, I can definitely appreciate working for a cause you believe in and I very much think that’s an idea you should consider further.

3) Working for people you interact with mostly online or with a significant time difference can definitely be challenging. (Some other thoughts on that here.) It sounds like your new managers are at least trying to make circumstances better. Not all managers do that. Just something to consider. If there’s anything they haven’t improved that you think could stand improvement, try to approach them about it in an open and honest fashion.

4) There is definitely a “grass is greener” bias when it comes to your current job versus a prospective job, especially when friends are involved. (Side note: working with friends isn’t always what it’s cracked up to be.) The trick is learning to recognize that bias in your own thinking and to judge each job as objectively as you can. One method that’s helped me is making a list of pros and cons for each job that I’m considering, current and prospective. This forces you to really consider what matters to you.

5) Every job change is a risk. It’s possible the place you’re going to will be worse in some aspect, be it personal happiness, job security, or something else. There are many things you won’t necessarily know unless you actually make the job change. Make a point of asking as many questions as you can of prospective employers during the interview process to get a sense of what working at a new job would be like. This may give you food for thought.

6) If you do decide to quit your current job, I would definitely avoid doing so before you have a new job lined up complete with an official offer letter. Also, depart from your current job as amicably as you can. You never know what bridges you may need to cross again; burning them can limit your options in the future.

7) To paraphrase Cal Evans, if you’ve tried to make positive changes in your job such that you can be happy, to little or no effect, you’re ultimately left with only two other options: a) suck it up, get on board, and do your job to the best of your ability; 2) leave. Again, that’s a choice only you can make.

PHPUnit + XHProf = BOOM!

I ran into an issue recently while trying to run PHPUnit tests in an environment using XHProf. Google didn’t prove to be much help, so I thought I’d document the problem and solution here for posterity.

When I ran my tests, each failed with the same cryptic error and no backtrace.

Attempted to serialize unserializable builtin class PDO

The cause was the culmination of two rather unfortunate circumstances.

First, XHProf uses a global array variable to contain its configuration in its auto_prepend_file, which includes a PDO instance. Yet another reason to add to the list of reasons why global variables are bad.

Second, PHPUnit has a setting called @backupGlobals that is enabled by default. For reasons of which I’m uncertain, the library used for this applies and reverses serialization to variables in the global state for the purpose of creating a snapshot of it.

The method used for detecting serializability in this library unfortunately doesn’t presently detect that PDO can’t be serialized when an instance of it is contained within a global array variable. As a result, when PHPUnit attempts to snapshot the global state after a test has run and it encounters the PDO instance from the XHProf configuration, the error above is the result.

There appears to be a pull request with changes to this serializability detection, though I can’t speak to whether it would improve it or cover the use case causing this problem. I’ve filed a related issue against the appropriate repo. Until it’s addressed, I’d recommend either disabling @backupGlobals in your phpunit.xml file or disabling XHProf when running PHPUnit tests.

Hope this helps someone else. Thanks for reading!

So Long, Blopboard

I began an amazing journey with Blopboard when I joined them in October 2013. Unfortunately, it looks like that journey is coming to an end. For reasons unrelated to the quality of the team (which has been awesome) or the product (which has also been awesome), the decision has been made to cease operations at the end of June. As such, I will be investigating new employment opportunities within that time period.

I’ve published an updated version of my résumé at http://elazar.github.io. I’d very much appreciate it if you, my dear readers, could review it, share it on social media, and point me to anyone who may have opportunities that would be a good fit for me. It’s worth noting that I’m located in New Orleans and have obligations that will keep me there for the foreseeable future, so opportunities I’m able to consider would have to be either local or remote-friendly. Thanks in advance for your help.

Positive thoughts and moral support that have been and will be given are appreciated. Here’s to the next chapter…

Speaking Resources

I was fortunate enough to recently be accepted to the inaugural php[world] conference. Shortly thereafter, a friend of mine within the community inquired about submitting to and speaking at conferences. I thought I’d post the resources I shared with them here for others interested in the same topic.

Getting Accepted

Whether you’re accepted to speak involves several factors. Being known within the associated community certainly doesn’t hurt, especially for the topic you’re submitting about. However, some conferences do like to have an infusion of fresh blood sometimes, so being new can also be a good thing.

One thing that often helps is submitting several proposals to the same conference. Conferences generally try to minimize the number of speakers they accept to keep costs down, so the more talks you’re able to give, the more conducive you are to that end goal.

Also related is your distance from the conference site or, more specifically, what it will cost to physically get you there. If you’re just starting out, aim for conferences that are held within your geographical area. Try to find a local user group at which to present your session. This can help in crafting the proposal and improving the presentation itself. After you’ve done so, post the slides online. In your proposal, link to them and mention which user group received your session and when. This helps give credibility to your session and your skills as a speaker.

The quality of your proposals is also a factor. Make sure you put adequate time and focus into crafting them. Keep them concise, but mention the objectives and high-level points of your session in sufficient detail to communicate your expertise in and enthusiasm toward the subject matter. Ensure they’re relevant to the conference’s target audience. Calls for papers often mention specific topics of focus; be sure to look for and review these.


Customizing Codeception Database Cleanup

Recently, I was looking into ways to speed up the runtime of the test suite at Blopboard. We use the Codeception framework to write functional tests for our REST API, part of which entails putting the database into a known state using Codeception’s Db module. The behavior of this module is similar to that of the PHPUnit Database extension with one exception: where PHPUnit only truncates tables and leaves their schemas intact, Codeception removes the database structure and expects the SQL dump it uses to recreate it between tests.

I must admit to not understanding this design decision of Codeception, nor attempts to clarify it. Be that as it may, I had a hunch that subverting it might lead to a faster runtime for our test suite, so I set about trying to find a solution to facilitate that. I found one, and while it’s a bit hacky, it works.


namespace Codeception\Module;

 * Extends the standard Db helper to override cleanup behavior so that tables
 * are truncated rather than dropped and recreated between tests.
class DbHelper extends \Codeception\Module\Db
	protected function cleanup()
		$dbh = $this->driver->getDbh();
		if (! $dbh) {
			throw new ModuleConfigException(
				"No connection to database. Remove this module from config if you don't need database repopulation"

		try {
			if (! count($this->sql)) {

			/** Start **/
			$dbh->exec('SET FOREIGN_KEY_CHECKS=0;');
			$res = $dbh->query("SHOW FULL TABLES WHERE TABLE_TYPE LIKE '%TABLE';")->fetchAll();
			foreach ($res as $row) {
				$dbh->exec('TRUNCATE TABLE `' . $row[0] . '`');
			$dbh->exec('SET FOREIGN_KEY_CHECKS=1;');
			/** End **/

		} catch (\Exception $e) {
			throw new ModuleException(__CLASS__, $e->getMessage());

The above module class is used in place of the Db module. To come up with it, I started by digging into the logic of the Db module class itself. Codeception has several hook methods for modules that it calls internally. One of these is _initialize(), which is called after the module class is instantiated and configuration for it is loaded but before any tests are run.

Looking at the _initialize() implementation in the Db module class, I found that it makes a call to a method to obtain a driver object for the particular database in use. This driver object implements a cleanup() method that the Db module class’s own cleanup() method calls between tests to handle resetting the database state.

There’s a problem here, though: the call to obtain the driver object is to a static method, which means there’s no way for me to specify my own logic for how to obtain a driver object rather than the logic that Codeception uses by default. This inhibits extensibility as well as testability.

I could have gotten around this by extending the Db module class and overriding its _initialize() method to call out to different code to obtain an instance of my own driver class. However, that would have meant duplicating most of the logic of that method, which is not of a trivial size. This would raise the likelihood that my code would not work with subsequent versions of Codeception if the method I was overriding changed.

In the end, the alternative I found was to instead extend the Db module class and override its cleanup() method. While this still results in duplication of code, the code being duplicated (which is demarcated by /** Start **/ and /** End **/ comments in the above code sample) is shorter, simpler, and less likely to be changed such that it impacts my code’s functionality. It is worth noting, however, that the above code sample will likely only work with MySQL, and would need modifications to work with other database servers.

Had the Db module class encapsulated its call to Driver::create() within an instance method, I could have simply overridden that method in my subclass and had a cleaner solution.

Alternatively, Codeception could have supported a solution like this:


namespace Codeception\Module\Db;

interface DriverFactoryInterface
  public function create($dsn, $user, $password);

  // ...

class DriverFactory implements DriverFactoryInterface
  public function create($dsn, $user, $password)
    // The contents of Driver::create() would go here.

namespace Codeception\Module;

class Db extends \Codeception\Module
  protected $driverFactory;
  protected $driver;

  public function _initialize()
    // ...
    if (!isset($this->config['driverFactoryClass'])) {
      $this->config['driverFactoryClass'] = '\Codeception\Module\Db\DriverFactory';
    $this->driver = $this->getDriverFactory()->create(
    // ...

  public function getDriverFactory()
    if (!$this->driverFactory) {
      $driverFactoryClass = $this->config['driverFactoryClass'];
      $this->setDriverFactory(new $driverFactoryClass);
    return $this->driverFactory;

  public function setDriverFactory(DriverFactoryInterface $driverFactory)
    $this->driverFactory = $driverFactory;

  // ...

In the above solution, there is a DriverFactoryInterface interface with, among others, a create() instance method, and a DriverFactory class that implements this interface. The Db module class allows the specification of a class that implements this interface via its configuration. It then handles instantiating this class and calls that object’s create() method from its _initialize() method rather than calling Driver::create() as it presently does. With this code in place, I could write my own class implementing the interface to return my own driver. This would allow me to accomplish my goal without having to resort to subclassing.

In any case, my hunch and solution paid off: with the solution in place, we were able to cut our test suite runtime by roughly 30%. Another pleasant side effect was that I no longer needed to maintain a copy of our database schema apart from the one we already maintain using Liquibase.

I hope this solution and my thoughts on Codeception’s present design are helpful to someone. Thanks for reading.

ledger basics and habits

Some time ago, I wrote about the CLI accounting program ledger, specifically building ledger from source and a small pet project of mine for visualizing ledger data called ledger-stats. I’ve even given a short presentation on the subject. I thought I’d take a blog post to talk about some basics of using ledger and some of the habits I’ve cultivated in my own usage of it. For more comprehensive info, check out the ledger manual.


A journal is a file in the ledger format. It can be of any size, though I typically like to keep one per year. This keeps file size relatively small (my largest one to date is about 150 KB), so searching them is easy, and makes it easy to report on annual figures (e.g. business expenses that can be written off) without having to manually specify date ranges.


An account is a label that represents a source or destination of money. A lot of people find this concept confusing because they associate it with an account at a bank rather than considering it as a more general concept. If you do any sort of computer programming, an account is similar in concept to a variable: it’s just a name with which a value (in this case an amount of money) can be associated.

ledger supports hierarchical organization of categories separated by colons, so you can have accounts like Expenses:Entertainment:Movies where Movies is under Entertainment and Entertainment is under Expenses. This allows you to have ledger report at various levels in the hierarchy so that you can see what you spend only on Movies or what you spend for all of your Entertainment.

Conventional top-level accounts are Equity, Liabilities, Assets, Expenses, and Income, but I only tend to use the last three myself. Assets are things I own that hold or are worth money, such as my bank accounts. Expenses are things I spend money on, such as food and housing. Income constitutes sources from which I obtain money, such as the salary I draw from my job or book royalty payments.


An event in which an account accumulates a quantity, such as adding $36 to your Expenses:Auto:Gas account to represent when you topped off your car’s gas tank today, is known as a posting. Because ledger uses a double-entry accounting system, however, you also need a posting representing the source of the funds from which you obtained this $36, such as the Assets:Checking account representing the checking account you have at your bank.

A transaction is two or more postings representing a financial event, such as the above example of purchasing gas, where the amounts balance out to zero. In a journal file, a transaction is formatted like this:

; Comment
Date Description
    Account1  Amount1
    Account2  Amount2

If you don’t include an amount for the last account, ledger will automatically use whatever quantity causes the transaction to balance to zero. Most transactions involve two postings, so the second quantity will often be the same as the first but with the opposite sign (i.e. negative or positive). Here’s what a real world transaction might look like:

2006/10/15 McDonald's
    Expenses:Dining  $5.36

In this instance, $5.36 is deducted (or debited) from the checking account and added (or credited) to the account for dining expenses. I often include things like initial posting dates (for which you can also use auxiliary dates) or transaction identifiers from paying bills in the transaction description (i.e. “McDonald’s” in the above example).

You may have a transaction where you’re spending money on several things that you want to track in separate accounts. Here’s an example of what that would look like:

2006/10/15 Exxon
    Expenses:Auto:Gas   $15.00
    Expenses:Cigarettes  $4.80

Here, money is being spent on gas and cigarettes and the total of the two expenses, $19.80, is what’s debited from the credit card account.

If you’re like me, most of your transactions will only involve two postings, like the first example of an expense above. Another common type of posting represents when you receive income, which might look like this:

2006/10/15 Employer
    Assets:Checking  $1000.00

These may look a bit odd when they show up in ledger reports because the quantities will be negative since you’re debiting the amounts from them. This is normal and you’ll get used to seeing it appear this way.

When a transaction clears your account, you can denote this by adding an asterisk delimited by spaces between the transaction date and description like so:

2006/10/15 * McDonald's
    Expenses:Dining  $5.36

One small potential pitfall: the segments of account names between colons can have spaces in them. As such, in order to denote the end of an account name, you should place at least two spaces between the account name and amount in each posting.

Reconciling accounts

I use a bank that offers online banking, including a web site and mobile app that allow me to view activity on my accounts almost any time. ledger may seem redundant in relation to something like this, but there are a few reasons why this isn’t so:

  • Transactions may not show up in the banking interface right away because some vendors often post several days’ transactions in a single batch every few days.
  • Transaction amounts may not be accurate. For example, initial postings from some gas stations may only show an amount of $1 and won’t reflect the correct amount until they clear. Initial postings from restaurants often don’t include any added gratuity.
  • Some activity may be fraudulent, which may not be immediately obvious if your card information is stolen in a business you frequent.

Every time I spend money and am able to get a receipt, I do so. I save them until they clear or, for larger purchases, until the period in which I may return the purchased item expires.

Tracking these receipts with ledger solves all of the above issues: I know about transactions that may not be reflected in my online account activity yet, I know when transaction amounts are inaccurate, and I can identity potentially fraudulent transactions when my online account activity shows a transaction for which I don’t have a receipt.

Every few days, I record any new receipts in my journal file, update any transactions that have cleared, and compare the journal file to my online account activity to ensure the two are consistent. This is a process called reconciliation.

Pro-tip: tracking cash withdrawals and purchases can be a pain. I typically just maintain an Expenses:Cash account for these sorts of things. If I can remember how I spent a bit of cash, great: I can just debit Expenses:Cash and credit the appropriate account for what I spent the cash on after the fact. Otherwise, I just leave the amount attributed to Expenses:Cash. It’s often not worth the trouble of trying to keep finer track of cash purchases than that.


The register command in ledger displays all postings from a journal file that involve one or more specified accounts. I typically use this to ensure that my journal and online account activity are consistent by listing all activity for those accounts.

$ ledger reg checking -f stan.txt
08-Jan-01 Opening Balances Assets:Checking     $1550.00 $1550.00
08-Jan-01 New Seasons Assets:Checking           $-60.91 $1489.09
08-Jan-01 Panda Express Assets:Checking          $-7.24 $1481.85
08-Jan-02 Sizzle Pie Assets:Checking             $-7.38 $1474.47
08-Jan-03 Kettleman Bagels Assets:Checking       $-7.60 $1466.87
08-Jan-03 Mio Sushi Assets:Checking              $-5.76 $1461.11
08-Jan-03 Eddie's Flat Iron P.. Assets:Checking $-22.26 $1438.85
08-Jan-04 Food Carts Assets:Checking             $-7.24 $1431.61
08-Jan-05 Burnside Brewery Assets:Checking      $-11.76 $1419.85
08-Jan-05 Tastebud Assets:Checking              $-42.13 $1377.72
08-Jan-07 Salary Assets:Checking               $1084.00 $2461.72

In the above example of register command output, “reg” indicates that the register should be used, “checking” is part of the name of the account being queried, and the value following “-f” indicates the journal file to be read.

The last column in the command output is a running sum of all quantities from postings for that account, where that column’s value in the last row of output is the account balance. The register command is useful when the balances of journal and online accounts aren’t consistent and you need to find where a transaction is missing or has an incorrect amount.

The output of the register command can be limited by transaction date (as well as many other options) using either or both of its “–begin” and “–end” flags like so:

$ ledger reg --begin=2014/02/01 --end=2014/02/28 -f stan.txt


Speaking of balances, ledger also has a balance command. This outputs balances for accounts at each hierarchical level. This allows you to see, for example, numbers as general as your total Expenses and as specific as what you spent going to the Movies.

$ ledger bal expenses -f stan.txt
$157874.79 Expenses
$5212.85   Entertainment
$21526.95   Food
$1377.36     Breakfast
$3174.59     Dinner
$11171.71     Groceries
$5803.29     Lunch
$8568.00   Insurance
$3948.00     Car
$4620.00     Medical
$5136.40   Interest
$4618.79     Car
$517.61     Student
$33600.00   Rent

Budgeting and Bills

ledger offers support for budgeting that can forecast when bills come due based on an interval and compare entered transactions against budgeted amounts to show whether you came over or under budget for any given time period. While this is a really cool feature, I don’t really use it the way it was intended to be used.

The budgeting feature uses what are called periodic transactions. These are slightly different from normal transactions in that they use a period expression rather than a date and have no description. They are formatted like so:

~ Monthly
    Expenses:Rent      $500.00
    Expenses:Food      $450.00
    Expenses:Auto:Gas  $120.00
    Expenses:Insurance $150.00
    Expenses:Phone     $125.00
    Expenses:Utilities $100.00
    Expenses:Movies     $50.00

I keep blocks of these at the top of my journal, organized by the pay period in which I pay the bills they each contain. When I sit down to pay bills, I make a copy of the appropriate block at the bottom of my journal and comment it out. Then, as I pay each bill, I uncomment the appropriate posting and move it out into an actual transaction. This gives me a way to easily check which bills haven’t been paid yet (e.g. if I’m waiting on a paper bill for the exact amount I owe).

I generally use vim to edit my journal files. There’s a contributed plugin for adding ledger format syntax highlighting, which is nice. I also use my fork of the visSum plugin for vim (the fork adds support for decimal quantities) to visually highlight and sum remaining bill amounts. I can compare the output of that with the balance of my account (from ledger’s register or balance commands) to determine how much I actually have left in excess of bills.


Even though I’m only using a very small amount of ledger’s overall feature set, I’m gaining several benefits by putting in the time to maintain these journal files:

  • Peace of mind in knowing that I know where my money is and where it’s going, despite whatever records my online banking interface may yet be lacking.
  • Information I can use to forecast future financial events, such as how much I’ll spend on electricity to keep my house at a comfortable temperature during the summer and winter, how much I spend on average for gas and groceries, or how much I spend on sporadic luxuries like eating out.
  • At tax time, I can very quickly and easily provide exact figures to my accountant for business-related expenses without having to take time to compile them manually.

Some people have asked me what I think of services like Mint. While such services may offer a nice interface, those interfaces are often intentionally kept very simple, to the point where I’m limited in the ways I can report on my data in their system. Additionally, there’s rarely a way for me to easily get that same data out of their system and into a format I can use elsewhere. In short, I like having control of my data and what I can do with it. These services may work for others, but they just don’t suit me.

Hopefully this blog post has given you some perspective on the benefits of accounting with ledger and how you can go about adopting this approach yourself if that prospect entices you. Feel free to leave a comment if you have a question or would like to discuss the topic further with me.

Travis and Composer and virtPHP, oh my!

I recently ran into an issue with one of my repos on GitHub when integrating it with Travis. When I installed dependencies with Composer and ran the PHPUnit tests on my local system running Ubuntu 13.10 and its stock PHP 5.5.3 apt package, they passed. However, I’d configured Travis to also do this under current 5.3 and 5.4 versions as well.

In the first build, everything worked fine under 5.4 and 5.5, but upon getting to the composer install instruction to install project dependencies and PHPUnit, the job for 5.3 failed with some rather unintuitive output from Composer that implied it didn’t recognize the platform package requirement that I’d provided for the minimum PHP version.

Your requirements could not be resolved to an installable set of packages.
Problem 1
- The requested package php could not be found in any version, there may be a typo in the package name.

Side note: While Travis does support Composer, the version of it available by default when running a job is frequently behind the current build. I’ve brought this up with them, but it doesn’t seem they’ve addressed it as of this writing. In any case, it’s easy enough to work around by including a composer self-update instruction as part of the build like so. This ensures that you won’t be affected by any recently fixed bugs in Composer.

Since the cause of my issue wasn’t immediately obvious from Composer’s output, my first thought was that I needed to begin my attempt at troubleshooting the issue by replicating it on my local machine. My second thought was that seemed like an abysmally miserable prospect, as it would require that I have several different versions of PHP installed other than the current one on my system.

I’d heard recently about a new project recently via Twitter called virtPHP that purported to be PHP’s answer to virtualenv for Python or rvm for Ruby. Thinking that my situation seemed a great use case for it, I proceeded to install it.

First, you have to read a bit past the cursory installation instructions on the landing page of virtPHP’s web site, particularly the “Using phpenv and php-build” section of virtPHP’s README file including the portion on package requirements. virtPHP doesn’t accomplish this great feat all on its own. It actually builds on two other existing projects by Christoph Hochstrasser, phpenv and php-build, and functions (in a rather PHP-like vein) more like glue to make working with these projects and managing what they produce easier. More specifically, it provides support for things like differing per-project PHP, PECL, and PEAR configurations.

In reality, all I ended up needing for what eventually proved to be a rather quick-and-dirty sort-of use case was phpenv and php-build alone, though I suspect virtPHP will be indispensable when I inevitably need a more long-term setup like this. Installing all three based on the installation instructions in their README files was fairly straightforward. I used them to create three PHP installations of the versions used on Travis (5.3.27, 5.4.25, and 5.5.9 as of this writing) and to quickly switch between them to re-run the composer install command that had failed on Travis, which consistently failed under 5.3 and worked under 5.4 and 5.5 on my local system.

Eventually, I opened the composer.json file and realized the problem: I’d misrecalled the minimum PHP version I’d set for my project to be installed as 5.3 when I’d actually set it to 5.4. Composer appropriately, though perhaps not intuitively, reacted by outputting errors when the local environment was running 5.3, a PHP version that did not meet the requirement I’d set in composer.json. In poking around, I found that this sort of user error is not entirely uncommon. Once I changed the requirement to 5.3 and pushed it to GitHub, the next Travis build succeeded for all PHP versions I’d specified.

So, thanks to the folks behind virtPHP for producing this project. I suspect I’ll be making more use of it in the future. :)

Sunshine PHP 2014

Due to uncertainties in my work schedule, I decided this year not to submit to the Sunshine PHP 2014 conference. However, the schedule turned out such that I was able to attend the conference. Many thanks to my awesome employer Blopboard for providing the time and funding to make this possible.

I was a speaker at the inaugural Sunshine PHP conference in 2013 and enjoyed that experience, but being able to just be an attendee this year was a nice change of pace. I’ve enjoyed the awesome content presented by excellent speakers.

With this year’s Sunshine PHP came a new member of the elePHPant family: Sonny, the yellow elePHPant. The process of obtaining your very own Sonny was a rather novel idea thought up by the hosts of Sunshine PHP. Attendees were given a bingo sheet where each space was branded for a sponsor and would visit the sponsor’s booth to get that space stamped. Once all spaces were stamped, you could turn in the bingo sheet to get Sonny.

In going through this process, I spoke with some very friendly guys at AppDynamics. I was able to learn more about their product, which is an alternative to the New Relic service I currently use at work for monitoring the performance of our product. I noted that AppDynamics isn’t currently listed in the Heroku add-on marketplace, but they clarified for me that it can still be installed on Heroku and pointed out related documentation.

The interaction was great and I’ll certainly be checking out the product further, but this quest to get Sonny is what instigated that conversation, which I think is awesome. Many thanks to Zend for sponsoring this fun activity within the conference.

Thanks to everyone who made Sunshine PHP a spectacular event to attend. I hope to see you again next year.

Sonny the yellow elePHPant


How to be a Great Developer

I’ve shamefully ripped off the title for this blog post from what I expect will be a phenomenal session given by Ed Finkler at php[tek] 2014. Sadly, I haven’t seen the session and may not be present at the conference. However, I’ve spoken with Ed and seen him give sessions on several occasions, so I know he sets the bar pretty high.

If you’ve not considered attending the conference and his session, I highly recommend doing so. Ed has plenty of insightful things to say about being a great developer. Today, the subject coincidentally crossed my mind and I realized I had a few things to say on the matter that may also prove useful to others.


A friend of mine who had some previous programming experience, but was not a developer, recently set out to learn more about what it’s like to be one. This led to many conversations between the two of us that reiterated a few things to me: this person is very intelligent and very determined, and while they felt quite out of their element in this pursuit, they were iteratively making progress toward their goal of understanding what being a developer is like.

This same person recently told me that, over the course of this pursuit, I was the only person they interacted with regarding it who, in their own words, didn’t make them feel like an idiot when they approached me. While I was glad that they had someone like this, I was sad that I was the only one who fit that description.

One thing I have learned over the course of my life is that “genius” is a very relative term: it all depends on who you’re sitting next to. I can name people who make me feel fairly smart just as I can name others who make me feel fairly dumb. I know these people well enough that I can say the latter don’t do this intentionally. Nevertheless, the sentiment keeps my ego in check, which I believe is a good thing.

We all start at the bottom of the totem pole. It benefits you to never forget this and to empathize with those who are going through that journey. Be open to learning from everyone, whether that person is perceived as a master or an apprentice. Nikola Tesla once said, “Our senses enable us to perceive only a minute portion of the outside world.” His words are worth remembering in the context of the limits of our individual knowledge.


Whether it’s with troublesome technology, difficult people, learning newbies, or even yourself as you go through the process of being a newbie once again to pick up a new skill, you will invariably feel frustrated by one thing or another in the process of being a developer. You have to learn to recognize when frustration is transient, when you can use it as motivation to continue and succeed, and to recognize when it’s a sign that you’ve done all you can do and that you should move on.


There is no silver bullet for anything. This applies as much to one’s technology stack or personal tool belt of choice as to one’s self. There is no state of enlightenment that a developer eventually attains. As cliché as it sounds, being a developer is more about the journey than the destination. The best goal you can set for yourself is to be better at what you do than you were yesterday. Compete with yourself, not with others. Aim to solve problems and be pragmatic, not dogmatic, in how you approach them.


These are things I’ve learned from my own experiences. I state them here realizing that they may be more applicable to me than they are to you. You must form your own opinions and find your own wisdom. I’ve said before what and who make me love what I do. I hope you share in some of that, and in some of what I’ve spoken about in this post. In the end, you must find your own path. Regardless of what that path is, I hope you find the passion shared by myself and those who inspire me and I wish you well in your pursuits.

Sniffing Outgoing HTTP Traffic on an iOS Device

I’m posting this mainly for my own benefit, but hopefully someone else finds this useful. A lot of people have recommended Charles as a debugging HTTP proxy for OS X. It looks like a great piece of software, but does require that you pay for it after 30 days. I was looking for something quick, easy, and free to analyze outgoing HTTP traffic on an iOS device.

Sadly, such options are few and far between, but if you’re willing to install a JDK (which Mavericks handily prompts you to do the first time you try to run a JAR file, and automates much of the installation process), they’re slightly better. I found a free edition of one called Burp Suite and managed to figure out how to get it working, so I’m documenting that here for future reference.

I’m using a Macbook Pro and an iPod Touch to do this, but since Burp Suite is written in Java, it should be possible to do this using any Java-capable device on your LAN and any device running iOS.

On the device with Burp Suite:

  1. Open up a terminal (I use iTerm2), run the ifconfig utility, and note the LAN IP address of the local machine (e.g.
  2. Run the Burp Suite JAR file from a terminal like so: java -jar burpsuite_free_v1.5.jar &
  3. In Burp Suite, click the “Proxy” tab and, within that, click the “Options” tab
  4. Under the “Intercept Client Requests” section, uncheck the checkbox marked “Intercept requests based on the following rules” if you don’t want to modify requests, only view them (because individual intercepted requests are blocked by the proxy until you manually opt to forward them from the “Intercept” tab within the “Proxy” tab)
  5. Likewise, under the “Intercept Server Responses” section, uncheck the checkbox marked “Intercept responses based on the following rules” if you don’t want to modify responses, only view them
  6. Under the “Proxy Listeners” section, select the existing entry in the table and click the “Edit” button
  7. In the “Edit proxy listener” window that appears, next to the “Bind to address” label, select the “Specific address” radio button and, from the drop-down menu next to it, select the LAN IP address of the local machine, then click the “OK” button
  8. Make sure the “Running” checkbox next to the proxy listener is checked, then click to the “History” tab within the “Proxy” tab

On the iOS device:

  1. From the desktop area, click the “Settings” icon
  2. In the “Settings” menu, click the “Wi-Fi” option
  3. In the “Wi-Fi” menu, click the option for your LAN
  4. Scroll to the bottom of the screen, find the “HTTP PROXY” section, and enter the LAN IP and port on which the HTTP proxy is running (8080 by default, you can find it in the proxy listener rule in Burp Suite)
  5. Return to the desktop area, then select the app for which you want to monitor HTTP traffic
  6. Perform some operation that kicks off an HTTP request, then find details on it in the Burp Suite “History” tab