If you ever ski at Mt. Baker Ski Resort you’re always treated to a view of Shuksan as you ride up Chair 8.
This summer, as I summited Ruth Mountain I was blown away by Nooksack Cirque. What an unexpected and stunning sight of a side of Shuksan you’d never know was there.
Ruth Mountain viewed from the trail up to Hannegan PassOn the summit of Ruth Mountain
The number of “RPC calls” is incredibly low on the list of things Twitter needs to fix to make more money.
The fact that it’s still running mostly fine speaks volumes to the engineers who have set it up.
Going with a metaphor, Elon just bought one of the most complex Formula 1 teams and has thrown out most of the people who know how to maintain the car.
Sure it performs great now. But with the FIFA World Cup looming let’s hope they don’t run into any issues cause the team that knows how to fix the one-of-a-kind gearbox is no longer available to you.
And everyone knows Calacanis has no idea how to drive.
If you’re really cool like me and own a decade old Dodge Grand Caravan you may find that the Totally Integrated Power Module (TIPM) will start failing.
In common cases (like mine) it will stop activating the fuel pump relay. This means your car will turn over but not start. This is not a great feature but does potentially help with the climate change crisis.
Fortunately for me I have a mechanic just down the street. I’d like to not tow this beast. To diagnose all of this the internet mechanics mention opening your TIPM and hot-wiring the fuel relay circuit. I took at the relay’s fuse and used a multimeter. No voltage. Spot checked some others and was reading the expected volts.
I shoved a jumper wire from my battery-wired cigarette port fuse (not the key activated one) over to the fuel pump relay fuse and heard what sounded like an electric motor activate from beneath the car.
I turned the ignition and the car started right up. Off to the mechanic.
Don’t Trust the Crazy Car Owner
I explained my morning’s adventures to the mechanic. He looked skeptically at my patch wire and said he’d run a test to diagnose things. Could be the TIPM, maybe just the fuel pump.
We both knew it was the TIPM. Turns out it was the TIPM. Shocker.
The fix: new TIPM. The problem: since these things fail so ofter and they’re year/make/model specific and there’s a worldwide computer chip shortage I get a refurbed one. Oh yeah, and it’s going to take a week to ship it.
Turns out no car for a week is fine. Thanks to COVID most necessities are all delivered now.
Bugs in the System
After some delays and fakeouts from TIPM dealers I got the call that the van was ready to go.
Walked up to the shop and it started right up. Settled the bill (ouch) and brought it home.
Next day the right turn indicator started flipping out “front right turn signal out”. Guess I get to make a stop at the auto supply joint. Looked up the bulb number but before making a purchase decided to physically check all the lights first. The front right fog light has been out for forever so I might as well fix that while I’m at it.
I activate the left turn signal: the left fog light starts flashing. What?
I activate the right turn signal: no lights flashing. Ok, expected.
I push the fog light button: bot turn signals turn on. What?
Quick call to the mechanic to describe the situation. Basically got the “wasn’t my fault” spiel which is fine, wasn’t casting blame just trying to problem-solve here.
There’s a lot of downtime at kid’s baseball games so between innings I start asking the internet what it thinks of all of this. Eventually I put in the correct series of search terms and land on someone having the same problem. I searched the document number in the images: “k6855837”.
It lands me on a YouTube video that take me step-by-step through the process of performing this fix.
Apparently my new-to-me TIPM has a firmware update that changed the behavior of some circuits. Just gotta flip some wires. Since it turned into a car maintenance day I took the opportunity to pick up some new H11 headlight sockets and wire them in since Dodge seems to use janky wiring that melts every few years.
And hooray, a car that starts with correctly functioning lights.
Not a complete list but these have not changed, even when being forced into environments that were actively hostile against me (WordPress PHP/JS code style is hideous).
GIF: team soft “g”
Tabs vs Spaces: spaces (but inserted via using the tab key, nobody presses the space key)
Pineapple: Excellent on a pizza when it also has Canadian bacon.
In a real-time chat workplace spelling and grammar tend to take a back seat to speed.
I typed qwerty proficiently for many years. After switching to Dvorak I have found that my fingers tend to translate the words I type phonetically.
I don’t know how to explain it. In my mind I’m using the word “their” but then I read back the sentence I just typed: “I don’t know there thoughts on …”. I’m always surprised. It’s not the word I had visualized but it’s the word I typed.
Sometimes I catch it but usually I hit enter before I read what I typed and quickly press up-arrow then e so I can quickly edit the grammatical error before too many coworkers have read it. (I just did it there. I know the word is “read” but my fingers type “red” and then I go back and fix it).
The scenario that always gives me problems is weather vs whether vs wether.
weather: the state of the atmosphere at a place and time as regards heat, dryness, sunshine, wind, rain, etc.: if the weather’s good we can go for a walk.
whether: expressing a doubt or choice between alternatives: he seemed undecided whether to go or stay | it is still not clear whether or not he realizes.
wether: a castrated ram.
I think I always get “weather” right but my fingers never want to type an “h” after the “w”. They just aren’t used to that sequence of keys.
So I end up talking about castrated rams much more than I ever thought I would.
For me everything worthwhile starts with “what if we try to …”. But the magic moment where that dopamine is flooding the brain coincides with that phrase: “Oh my god, this is gonna work.”
There will no doubt be a million more things to do, but thats the moment the “how” starts falling into place.
The second in a series of posts that investigates using strongly-typed first-class functions with WordPress WP-API to create a composable, testable, verifiable, and productive method of REST API development.
Context switching is a productivity killer. What exactly constitutes a context switch though?
Moving to a ping in Slack away from a Vim window? Definitely a context switch.
Switching via cmd-tab between a source code editor and browser window? Also a context switch. Yes, even when duck-duck-going the error from the console.
Everything that reduces context switching during development is a productivity win.
Debugging is a Productivity Killer
Time spent searching logs and reconstructing failure cases from production bugs is time not spent shipping.
It is also time that was not accounted for in the 100% accurate development estimate given to the project manager to complete the task.
Passing a string value to a function that expects an int: bug. Typing the incorrect string name of a function in WordPress’s add_filter: another bug. Calling a method on a WP_Error instance because it was assumed to be a WP_User: bug.
They may all seem like small bugs but they can quickly add up to a non-trivial amount of time debugging. Perhaps these bugs will be discovered quickly at runtime, but that requires the correct codepaths are executed in a runtime. Is every code path in a project going to be executed between each source code change? No.
Static analysis will increase productivity by uncovering these bugs. But even with a 100% typed, fully analyzed codebase validating running code output is still necessary.
Automating runtime validation is another tool to increase productivity.
Runtime Verification
Psalm enforces correct types and API usage. Checking the correctness of the runtime code still requires some manual steps, like booting up an entire WordPress stack. Previously, wp-env was used to verify that the endpoint actually worked.
This isn’t going to scale well when the number of endpoints and the number of ways to call them increases. Jumping from an editor to a browser and back isn’t the best recipe for productive coding sessions either.
Time for automated tests.
In the world of PHP, that means PHPUnit.
The bare minimum code to test totes_not_buggy() is a single implementation of PHUnit\Framework\TestCase with a single test method. It will live in tests/Totes/TotesTest.php:
<?php
namespace Totes;
use WP_REST_Request;
use WP_REST_Server;
class TotesTest extends \PHPUnit\Framework\TestCase {
/**
* @return void
*/
function testTotesNotBuggy() {
$request = new WP_REST_Request( 'GET', '/totes/not-buggy' );
$response = totes_not_buggy( $request );
$this->assertEquals( [ 'status' => 'not buggy' ], $response->get_data( ) );
}
}
To run PHPUnit, the dependency needs to be installed.
composer --dev require phpunit/phpunit
Now run the test:
./vendor/bin/phpunit tests
// yadda yadda
ERRORS!
Tests: 1, Assertions: 0, Errors: 1.
The error shows that we don’t have WordPress APIs available to our run runtime:
1) Totes\TotesTest::testTotesNotBuggy
Error: Class 'WP_REST_Request' not found
WordPress is a dependency of this project. It won’t work without it. Time to install it:
composer require --dev johnpbloch/wordpress
The johnpbloch/wordpress package by default will install the WordPress source code in ./wordpress. Setting up a whole WordPress stack to work on some source code: productivity killer. “No install” is faster than any five minute install no matter how famous it is.
If WordPress were a PSR-4 compliant project there wouldn’t be anything left to do. But it isn’t. To illustrate, run the test again and observe the result is the same.
Since Composer doesn’t know how to autoload WordPress source code, PHPUnit needs to be taught how to find WordPress APIs during test execution. A perfect place for this is via PHPUnit’s "bootstrap" system.
Generate a config and tell PHPUnit to use a custom"bootstrap":
./vendor/bin/phpunit --generate-config
PHPUnit 9.0.1 by Sebastian Bergmann and contributors.
Generating phpunit.xml in /Users/beau/code/wp-api-fun
Bootstrap script (relative to path shown above; default: vendor/autoload.php): tests/bootstrap.php
Tests directory (relative to path shown above; default: tests):
Source directory (relative to path shown above; default: src):
Generated phpunit.xml in /Users/beau/code/wp-api-fun
This generates ./phpunit.xml and tells phpunit to run test/bootstrap.php before executing tests.
Time to hunt down all of the WordPress dependencies for this test.
One way to find which PHP files need to be included is to keep running the tests and including the files that define the missing classes and functions.
For example, the current error is that WP_REST_Request is not defined.
Now add wordpress/wp-includes/rest-api/class-wp-rest-request.php.
Keep going until it passes. This is the end result for now. Note that this is – at this time in our development – 100% of our plugin’s runtime dependencies.
Now that Composer can install WordPress and PHPUnit, the CI can run these tests too. Add it to the GitHub action:
+
+ - name: Unit Tests
+ run: vendor/bin/phpunit
Runtime verification of any new route can now be captured in a unit test. Once in a unit test it can be ran in all sorts of ways.
Bonus, with XDebug configured PHPUnit will also report coverage analysis when proper @covers annotations are added:
vendor/bin/phpunit test --coverage-html coverage-report
PHPUnit 9.0.1 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 68 ms, Memory: 8.00 MB
OK (1 test, 1 assertion)
Generating code coverage report in HTML format ... done [12 ms]
68 millisecond execution time with 100% coverage of a one-line function assigned a CRAP score of 1. Gotta love that new project smell.
Screen capture of a PHPUnit coverage report.
Safety Nets Engaged
Between Psalm and PHPUnit we now have static analysis and automated runtime tests.
Next up we’ll dive into Higher-Order Kinds with Psalm and start using them with WP-API to create a declarative, composable API.