First Hire After the Pitch
Oxygen Ventures had just won a startup pitch in Melbourne. I was the first developer they hired. There was no team to join. There was no codebase to inherit. There was a desk, a laptop, and a list of things that needed to exist.
I was twenty-two. I didn't know what I didn't know, which turned out to be an advantage. When nobody tells you how things are supposed to be done, you figure it out by doing them. Every decision was mine to make. Every mistake was mine to learn from. That's a terrifying amount of freedom when you're fresh, but it's also the fastest education you can get.
Melbourne's startup scene in 2015 was small and scrappy. Nobody had huge budgets. You wore every hat because there was nobody else to wear them. I was the frontend developer, the backend developer, the DevOps person, and occasionally the one explaining technical constraints to stakeholders who didn't want to hear about technical constraints. That compression -- doing everything, all the time -- is where the instincts started forming.
The Unified Interface
Capitol Grand Hotel needed a social media feed on their WordPress site. Pull posts from Facebook, Twitter, Instagram, and their WordPress blog. Display them in a unified stream. Simple enough on paper.
It was not simple.
In 2015, these APIs were a mess. Facebook's Graph API required OAuth tokens that expired unpredictably and returned nested JSON structures that changed shape depending on which fields you requested. Twitter's REST API had aggressive rate limits and a completely different authentication model. Instagram's API was severely restricted -- limited endpoints, limited data, limited everything. And WordPress was its own thing entirely, with its own conventions that matched none of the others.
Four platforms. Four different authentication flows. Four different data shapes. Four different rate limiting strategies. Four different failure modes. The naive approach is to write four separate integrations and stitch the results together. That works until it doesn't. It doesn't scale. It doesn't handle failure gracefully. And every time one platform changes its API -- which they did constantly -- you're rewriting integration logic that's tangled with presentation logic.
What I built instead was a single interface. One contract that all four platforms conformed to. Each platform got an adapter that translated its specific chaos into one clean, normalized shape. On the other side of that interface, the consuming code didn't know or care whether a post came from Facebook or Instagram. It just knew it had a post with a timestamp, content, media, and a source. One shape. Four messy realities behind it.
This wasn't a pattern I'd read about in a textbook. I built it because the alternative -- four special cases threaded through the entire codebase -- felt wrong. It felt fragile. The unified interface felt right. It was the first time I experienced the satisfaction of making a messy problem disappear behind a clean abstraction.
I've used that same instinct on every messy system since. At TitanFX, the developer tools I built were translation layers that normalized inconsistent internal systems behind single contracts. The pattern is the same. Find the chaos. Design the interface. Make the mess invisible to everything downstream. I learned it at twenty-two, building a social media feed for a hotel. I still reach for it a decade later.
Docker Before Docker
ECAL was the bigger project. A calendar widget platform that needed modernizing from Zend Framework and jQuery to Laravel and AngularJS. The stack migration was significant, but the decision that raised the most eyebrows had nothing to do with frameworks.
I introduced Docker. In 2015.
This needs context. In 2015, Docker was not what it is now. Docker-compose didn't exist in any mature form yet. The documentation was incomplete. The tooling was rough. Almost nobody in Melbourne's startup scene was using it. When I proposed it, the reaction was polite skepticism. Why are we adding this complexity? What's wrong with MAMP? Why can't we just deploy the way we've always deployed?
What was wrong was "it works on my machine." Every developer had a slightly different local environment. PHP versions didn't match. MySQL configs differed. Something would work locally and break on staging. We'd spend hours debugging environment differences instead of building features. The problem was real. The solution was unproven.
I pushed for it anyway. Not because I wanted a resume bullet. Because I'd spent enough hours debugging environment parity issues to know that the pain of adopting Docker was less than the pain of not adopting it. Setting it up was rough. Teaching the team was slow. There were days when the skeptics seemed right. But once it worked -- once every developer had identical environments, once deployments became predictable, once "it works on my machine" stopped being a sentence anyone said -- the bet paid off.
Being right about a technology before consensus forms is a specific feeling. It's not vindication. It's relief. You spent weeks defending a decision you couldn't fully prove, and then one day it just quietly becomes the obvious choice that nobody questions anymore. I've chased that feeling since. Sometimes I've been right. Sometimes I haven't. But the willingness to bet on unproven tools when they solve a real problem -- that started with Docker in 2015.
Gherkin for Non-Devs
The other thing I introduced at ECAL was Codeception with Gherkin syntax for the test suite. This sounds boring. It wasn't.
Gherkin lets you write test specifications in plain English. "Given I am on the calendar page. When I click the export button. Then a download should start." A developer can read that. A non-developer can also read that. That was the point.
The QA team at ECAL weren't developers. In most shops in 2015, QA got handed test scripts written by developers and told to follow them step by step. They were executors, not participants. They didn't understand what the tests were actually verifying. They couldn't suggest new test cases because they couldn't read the existing ones. They were locked out of the process by the language it was written in.
I wrote the test specs in Gherkin so the QA team could read them. And once they could read them, they started contributing. They'd spot gaps. "What about when the user has no events? What about when the timezone changes?" They knew the product better than the developers did in many ways. They just hadn't been given a format that let them participate.
The principle is simple: make non-technical people part of the technical process. Don't gate-keep with jargon. Don't assume that because someone can't write code, they can't contribute to technical decisions. Find the format that includes them. I didn't have a name for this instinct in 2015. I just knew that the QA team had knowledge we were wasting, and the fix was a different syntax.
The Throughline
These weren't achievements on a resume. They were instincts forming.
The instinct to normalize chaos into clean abstractions. The instinct to adopt tools before consensus, when they solve a real problem. The instinct to include non-technical people in technical processes by meeting them where they are. I didn't have language for any of this in 2015. I just built things and noticed what worked.
The technology from that year is dead. AngularJS is dead. That version of the Facebook API is dead. The specific PHP patterns I wrote are patterns I'd never write today. But the instincts aren't dead. I still reach for the unified interface when I encounter messy systems. I still bet on unproven tools when the pain they address is real. I still look for ways to include people who are being excluded by unnecessary technical barriers. The code from 2015 is long gone. The patterns survived.
Related Reading
- Pioneering Containerization: My Docker Journey in 2014 — The thesis project where the Docker bet started, one year before Oxygen Ventures