# Glenn Gillen - AI Strategy
## Designing Agents That Close Loops, Not Just Tasks
In the world of AI, there's a critical distinction between agents that simply complete tasks and those that close entire loops. The latter is where true value lies.
## The Difference Between Tasks and Loops
**Tasks:** These are individual actions that need to be performed. An agent might send an email, generate a report, or book a meeting.
**Loops:** These involve the entire process from initiation to completion. A loop might start with identifying a need, executing the necessary tasks, and ensuring the desired outcome is achieved.
## Why Closing Loops Matters
When an agent can close a loop, it means less oversight, fewer dropped balls, and more consistent outcomes. In short, it's the difference between simply doing and truly delivering.
## Designing Agents That Close Loops
1. **Clear Goals:** Define what a completed loop looks like. The agent needs to know not just the task, but the intended outcome.
2. **Feedback Loops:** Build in mechanisms for the agent to check if the desired outcome has been achieved. This might involve gathering data or seeking confirmation from stakeholders.
3. **Autonomy and Decision-Making:** Empower agents with the ability to make decisions within certain parameters. This allows them to adapt and ensure the loop is closed effectively.
By designing agents that close loops, you're not just streamlining tasks — you're ensuring the entire process is taken care of, leading to more reliable and impactful outcomes.
## How AI Gives CEOs Their Time Back
Jeff Bezos once said:
> If I make three good decisions a day, that’s enough. They should just be as high quality as I can make them.
But here’s the problem: most CEOs never get the time or clarity to focus on even one.
The average executive day is consumed by low-leverage work:
* Digging through emails for context
* Prepping for back-to-back meetings
* Trying to recall which document, app, or thread held the key point
AI — when applied correctly — doesn’t just automate tasks. It curates decision readiness:
* It surfaces the one message that needs to land in your next meeting.
* It summarizes the last six months of Slack threads so you can walk in with clarity.
* It watches your workflows and spots patterns you’re too busy to notice.
This isn’t about replacing EAs or chiefs of staff. It’s about giving them superpowers.
And giving you back the most valuable resource of all: uninterrupted attention.
Because when AI handles the noise, you’re free to focus on the signal.
## The Anatomy of a Useful AI Agent
# The Anatomy of a Truly Useful AI Agent
Creating AI agents that are genuinely transformative requires a deep understanding of their essential components. Let's break down the anatomy of a truly useful AI agent.
## 1. **Clear Purpose**
A useful AI agent starts with a clear, well-defined purpose. Whether it's automating a workflow, providing insights, or managing tasks, the agent's role should be crystal clear.
## 2. **Robust Data Access**
An agent is only as good as the data it can access. Ensure your agent has robust, reliable data sources and can interpret that data effectively.
## 3. **Contextual Awareness**
Context is key. A truly useful agent understands the environment it's operating in — whether it's the workflow, the user’s preferences, or the business goals. This allows the agent to make more relevant and accurate decisions.
## 4. **Adaptive Learning**
A great agent continuously learns and improves. By leveraging machine learning and user feedback, the agent can adapt to new scenarios and become more effective over time.
## 5. **Autonomy with Boundaries**
While autonomy is important, a truly useful agent also operates within defined boundaries. This ensures it doesn't overstep or make decisions beyond its scope.
By focusing on these core components, you can design AI agents that not only perform tasks but truly transform workflows.
## From Inboxes to Outcomes: What AI-Native Work Replaces
Executives don’t need more notifications.
They need more clarity.
But almost every tool we use today, from email to task managers to Slack, still operates on the **inbox model**:
- Work shows up as a message
- You triage it manually
- You track it across systems
- And you hope something gets done
It’s reactive, fractured, and noisy. And it’s exactly the kind of thing AI shouldn’t just **optimize** — it should **replace**.
## The Shift: From Inboxes to Outcomes
| **Old Workflow** | **AI-Native Alternative** |
|------------------|---------------------------|
| Inbox zero | Outcome alignment |
| Calendar invites | Decision prep flows |
| Status update meetings | Auto-curated progress snapshots |
| Manual follow-ups | Autonomous agent loops |
| Searching through threads | Surface what's changed, and why |
| Flagging action items | Detecting stalled workflows |
| Checking docs for updates | Prompting action from deltas |
| Keeping personal notes | Persistent, contextual memory |
## This isn’t just automation.
## It’s a redefinition of where and how work happens.
In the inbox era:
- You’re the integrator
- You’re the router
- You’re the one chasing clarity
In the AI-native era:
- The system curates what matters
- It tracks and nudges progress
- It shows up when the moment to act is right
## The inbox trained us to think of work as *volume*
## The outcome model forces us to focus on *leverage*
AI-native tools aren’t impressive because they summarize 50 Slack messages. They’re impressive because you **never needed to see those 50 messages** in the first place.
They reduce surface area. They create decision clarity. And they let you focus on the part only you can do: judgment.
If your tools still treat every task like a message and every message like something you have to read... you’re not working with AI.
You’re just working faster in the wrong direction.
## Meeting Prep, Reimagined: What Executives Actually Need
Most AI meeting prep tools today do the same thing:
They skim the invite, scan your inbox, and generate a polite summary of what's happening.
Useful? Sure.
But strategic? Not even close.
The job of a CEO — or any executive — isn't to remember what's on the agenda.
It's to walk into the room ready to **land a message**, **make a decision**, or **influence an outcome**.
And *that's* where most tools fall short. Because true meeting readiness means answering three very different questions:
## 1. What's the message I need to land?
Every meeting is an opportunity to shape momentum. A good prep flow highlights the **key narrative or talking point** you need to reinforce, not just the topic of discussion.
* "This partnership is a trust signal for future investors."
* "This hire will reset the culture around execution."
* "We're not aligned on pricing strategy — steer the group."
## 2. What's the decision I'm expected to make?
A calendar event doesn't tell you this. But a good prep system surfaces the **decision gravity** of a meeting:
- Approve the plan or ask for a rethink?
- Commit budget or push for alternatives?
- Choose speed or alignment?
Knowing the likely fork in the road before you walk in changes everything.
## 3. Do I have the context I need to act with confidence?
This is where AI shines — when it can curate **just enough** background, not an info dump:
- A one-paragraph recap of last quarter's OKRs
- A pull quote from the investor's last email
- The decision history of similar past meetings
The best prep isn't exhaustive — it's **trust-building**.
When you combine these three layers — message, decision, and context — you move from being *present* in a meeting… to being *effective* in it.
Because in the end, meetings aren't about attendance.
They're about leverage.
## The Role of AI in Decision-Making: How Leaders Can Leverage AI for Better Outcomes
In today's fast-paced world, leaders need every advantage they can get. AI is becoming a crucial tool in the decision-making process. Here’s how leaders can leverage AI for better outcomes.
## 1. **Data-Driven Insights**
AI can process vast amounts of data quickly, providing leaders with accurate, data-driven insights that inform better decision-making.
## 2. **Predictive Analytics**
With AI's predictive capabilities, leaders can anticipate future trends and outcomes, allowing them to make proactive and informed decisions.
## 3. **Risk Management**
AI can identify potential risks and provide simulations, helping leaders to mitigate issues before they become significant problems.
## 4. **Enhanced Efficiency**
By automating routine data analysis, AI frees leaders to focus on strategic decisions that require human insight and creativity.
AI is not a replacement for leadership, but a powerful ally in making better, more informed decisions.
## AI-Native Tools vs. AI-Assisted Tools: What's the Real Difference?
In the evolving landscape of AI, the terms "AI-native" and "AI-assisted" are often used interchangeably. But there's a fundamental difference between the two. Let's break it down.
## AI-Assisted Tools
**Definition:** AI-assisted tools are traditional tools enhanced by AI features. They rely on human input and use AI to streamline or improve specific tasks.
**Examples:**
- **Email filters:** AI helps prioritize important messages.
- **Grammar checkers:** AI suggests improvements, but the human still writes the content.
**Key Characteristics:**
- **Human-Centric:** The human is in control, with AI providing support.
- **Incremental Improvement:** These tools make existing workflows more efficient but don't fundamentally change them.
## AI-Native Tools
**Definition:** AI-native tools are built from the ground up with AI at their core. They don't just assist — they fundamentally change how work gets done.
**Examples:**
- **AI workflow managers:** These tools can autonomously manage entire processes, not just assist with parts of them.
- **Decision-making agents:** These can analyze data and make recommendations or decisions autonomously.
**Key Characteristics:**
- **AI-Driven:** These tools are designed around AI capabilities, not as an add-on.
- **Transformational Impact:** They change the way work is done, often eliminating or redefining traditional workflows.
## The Real Difference
The key difference lies in the level of integration and the degree of transformation. AI-assisted tools improve existing processes, while AI-native tools redefine them entirely. By understanding and leveraging this distinction, you can better position yourself to take full advantage of the AI-native revolution.
## The Key Traits of AI-Empowered Leaders
As AI reshapes the workplace, the leaders who thrive are those who embrace and leverage AI. Here are the key traits that define AI-empowered leaders.
## 1. **Data Savviness**
AI-empowered leaders are comfortable with data. They know how to interpret AI-generated insights and use them to inform strategic decisions.
## 2. **Adaptability**
The AI landscape evolves quickly. Leaders who thrive are those who embrace change, continuously learn, and adapt their strategies accordingly.
## 3. **Empathy and Collaboration**
Even with AI, the human element remains crucial. Successful AI-empowered leaders excel in empathy and collaboration, fostering a balanced and inclusive workplace.
## 4. **Vision for the Future**
AI-empowered leaders have a clear vision of how AI can transform their industry. They are forward-thinking and proactive in integrating AI into their strategic planning.
By cultivating these traits, leaders can effectively harness the power of AI and lead their organizations into the future.
## AI Is Not Your Assistant. It's Your Process.
Most people still talk about AI like it's a better version of a human assistant:
- "It can summarize your notes."
- "It can send follow-ups."
- "It can book your meetings."
Useful? Sure.
But the metaphor is wrong. And it's limiting our ambition.
**AI is not your assistant.**
**AI is your process.**
## Assistants wait for instructions.
## Processes define the work.
An assistant needs you to say:
> Book a meeting with Sarah next week.
A process understands:
- You're trying to close a partner deal.
- You always meet on Tuesdays.
- Sarah hasn't replied in 3 days.
- There's a board report due Friday.
And it **books the meeting** in a way that moves your goals forward — without asking you for micro-decisions.
That's not assistance.
That's **orchestration**.
## The assistant metaphor is reactive.
## Process-native AI is proactive.
We've been building tools that wait for prompts.
But real leverage comes when the system anticipates needs, handles edge cases, and quietly removes friction.
- Not "write a summary" — but **"Here's what's changed since last time."**
- Not "send an email" — but **"The message has already gone out. Here's a copy."**
- Not "check in with the team" — but **"Here's what's blocking progress."**
## The real shift isn't speed. It's scope.
Assistants accelerate tasks.
Processes eliminate them.
The real promise of AI-native work isn't shaving minutes off a workflow — it's removing the
workflow entirely. If the system knows what needs to happen, and why, and when… **you never needed the task in the first place.**
This is the difference between automation and autonomy. Between asking for help and operating at a higher level.
**AI shouldn't be copying your old process.** It should be **replacing it** — with something better.
## AI and the Future of Team Dynamics: Building High-Performing Teams with AI Tools
The workplace is evolving, and AI is playing a pivotal role in shaping how teams collaborate and perform. Here’s how AI is influencing the future of team dynamics.
## 1. **Enhanced Collaboration**
AI tools can analyze communication patterns and suggest improvements, helping teams communicate more effectively and efficiently.
## 2. **Data-Driven Insights**
AI provides real-time data on team performance, allowing leaders to make informed decisions and tailor strategies to maximize productivity.
## 3. **Personalized Workflows**
With AI, teams can enjoy personalized workflows that cater to individual strengths and preferences, boosting overall team morale and efficiency.
## 4. **Proactive Problem Solving**
AI can predict potential issues and provide proactive solutions, helping teams stay ahead of challenges before they escalate.
AI isn't just a tool; it's a team player that can elevate collaboration, productivity, and performance to new heights.
## The Future of Work: How AI is Shaping the Next Generation of Leaders
As AI becomes integral to the workplace, it's not just productivity that's changing — it's leadership. Here's how AI is shaping the next generation of leaders.
## Data-Driven Decision Making
Tomorrow's leaders will be adept at interpreting AI-generated insights, making data-driven decisions a cornerstone of effective leadership.
## Empathy and AI Collaboration
While AI can handle data and routine tasks, the leaders of the future will excel at empathy and collaboration, ensuring that AI tools complement human strengths.
## Continuous Learning
With AI evolving rapidly, future leaders will prioritize continuous learning and adaptability, leveraging AI to stay ahead of the curve.
In embracing these changes, AI is not just a tool — it's a catalyst for a new kind of leadership.
## How AI Agents Are Redefining Executive Productivity
In the modern workplace, executives are inundated with tasks that can distract from strategic decision-making. AI agents are stepping in to redefine what productivity looks like at the executive level.
## From Routine to Strategic
AI agents handle routine tasks — scheduling, emails, data gathering — freeing executives to focus on strategic initiatives.
## Enhanced Decision-Making
By providing real-time insights and data analysis, AI agents empower executives to make informed decisions quickly and confidently.
## Personalized Assistance
AI agents can learn individual preferences and workflows, providing a personalized productivity boost that aligns with each executive’s style.
In short, AI agents are not just about doing more — they're about doing what truly matters. And that’s the ultimate game changer for executive productivity.
## What Makes an AI Agent "Actually Useful"?
We don’t need more agents that can "summarize PDFs" or "book restaurants."
We need agents that help people get real work done — consistently, reliably, and intelligently.
Here’s what separates useful agents from gimmicks:
## ✅ 1. Persistent memory, not prompt amnesia
A useful agent understands not just this task—but the why, the when, and the who behind it. Context is king.
## ✅ 2. Opinionated defaults
Decision fatigue kills adoption. Good agents don’t ask 100 questions—they make smart assumptions, and only ask when necessary.
## ✅ 3. Resilience to ambiguity
Useful agents handle the weird edge cases: late replies, missing files, changed calendars. They don’t just throw errors—they adapt.
## ✅ 4. Workflow awareness
They know what happens before and after the task. They aren’t just performing actions—they’re orchestrating outcomes.
## ✅ 5. Trustworthy enough to delegate to
Real usefulness starts when users stop watching over the agent’s shoulder. That requires trust by design—clear explanations, visibility into decisions, and predictable behavior.
If your agent can’t reliably help a user close a loop—from intention to outcome—it’s not useful. It’s a novelty.
And users are done playing with novelties.
## The Three Types of Useful Agents (And Where They Win)
In the realm of AI-native workflows, not all agents are created equal. Understanding the different types of agents — and where they shine — can make all the difference in building truly effective AI systems.
## 1. **Orchestration Agents**
**What They Do:**
Orchestration agents manage and coordinate complex processes, ensuring every step is executed in the right order and at the right time.
**Where They Win:**
- **Project management:** These agents excel in coordinating tasks, ensuring deadlines are met, and keeping everyone on the same page.
- **Event planning:** They thrive in environments where multiple dependencies need to be managed seamlessly.
## 2. **Insight Agents**
**What They Do:**
Insight agents specialize in analyzing data, identifying patterns, and offering actionable recommendations.
**Where They Win:**
- **Market analysis:** They can quickly parse through vast amounts of data to provide meaningful insights on trends and opportunities.
- **Decision support:** They help executives make informed choices by presenting the most relevant information at the right time.
## 3. **Action Agents**
**What They Do:**
Action agents are all about execution. They handle tasks from start to finish, reducing the need for human intervention.
**Where They Win:**
- **Customer service:** These agents can handle routine inquiries, process transactions, and resolve issues autonomously.
- **Routine operations:** They excel in environments where tasks are repetitive but crucial for maintaining smooth operations.
---
By recognizing which type of agent suits a particular scenario, you can design AI-native workflows that are not only efficient but also truly transformative.
---
## Stop Bolting On AI. Start Building With It.
Most AI startups today follow a familiar pattern:
1. Take an existing app.
2. Add a chatbot.
3. Call it "AI-powered."
This is the bolt-on mentality — AI as frosting on the same old cake.
But real transformation doesn’t come from sprinkling in GPT-4.
It comes from rethinking the entire workflow around what AI enables:
* What would this process look like if intelligence was ambient?
* What if the app proactively shaped the next best action?
* What if decisions didn’t require hunting for context?
AI-native tools don’t just answer questions—they change the nature of the work:
* They collapse complexity.
* They eliminate busywork entirely.
* They shift users from "doing" to "deciding".
The difference is night and day.
One gives you a chatbot that rewrites your email.
The other makes it so you never needed to write the email at all.
# Glenn Gillen - Thoughts & Musings
## Appreciating the craft
As I pull into Flinders Street Station in Melbourne, I make a dash for the
Degraves Street subway exit. Everyone else floods out of the beautiful and
ornate arches at either end of the platforms, so it's a rare chance to escape
the crowds at such a busy interchange. Descending into the depths you can
imagine what it was like back in the 1920's when it was the busiest passenger
station in the world. The art deco finishes. The (now off) white tiling. The
arched roof. It's a really beautiful piece of architectural history for the city.
Flinders Street Station, Melbourne. Photo credit: Scott Cresswell
As I come through the barriers I stop by Cup of Truth to grab my first coffee for the day. Barely more than a podium built into a wall, a giant man lurches forward to ask me what I'd like. "Just a long black, please". "Oh, it's a Colombian today. White honey-washed process, it's sweet with some light nutty notes toward the finish. I think it's a really special one". A minute or so later he hands the coffee over to me with his giant paw. I take a small sip. Cup of truth indeed, it was a really special one. In a city with a ritualistic appreciation for coffee, and a consistently good product to service it, it's easy to become complacent. But occassionally there's that jolt of experience to make you truly appreciate it again. We exchange an appreciative nod, and I make my way out of the subway.
A delicious coffee and friendly service means I have a little more spring in my step and a brighter light in my eyes, I take a small detour into Degraves Street on my way out of the subway. The chaos of the main streets can wait for an extra two minutes as I nagivate the cobbled lanes for which Melbourne has become famous. A constantly changing immersion of vibrant street art, it's impossible to not marvel at the sheer talent and artistry of some of the people that live here (alongside the occassional brand name foreign street artist). Even when it's grey and raining it's a beautiful place to hide under an awning. Watching the water cascade down the bluestone. Over the art. As it bounces across the various covers the cafés have out to protect their patrons. A sea of colour dancing below them as the army of workers and their umbrellas navigate the passage to their offices.
Degraves Street, Melbourne. Photo credit: Tina Reynolds
But that's not today. Summer has arrived, and instead the morning sun is squeezing down the lane and making the colours on the wall pop. A spotlight moving slowly across a vast canvas. I pause to breathe it all in, for just a moment. Inspired, I head for my tram.
I board the 59 toward North Melbourne. A B2 class tram with all the hallmarks of the utter focus on utility that washed in with the start of the 80's. It feels like the public transit interpretation of Brutalist architecture in many ways, except with air-conditioning and electronic signs. It has little of the beauty a W class but it's infinitely more practical. Especially during a sweltering Melbourne summer.
It takes me almost to the door of the Meat Market, my host for the rest of the week while I'm at a conference. Another stunning example of architectural history. And a reminder of how long modern commerce has been occurring in this city. A huge barrel vault ceiling. Along with a design that was optimised for the horse & cart trade at the time. There's still hallmarks of their impact on the building, with grooves worn into the kerbs in various places from the gradual wearing down from cart wheels over its 140 year history.
Inside the old Melbourne Meat Market. Photo credit: CSSConf Australia
The next three days I'm eagerly absorbing the passion and energy of the people around me who spend their days creating things. People on stage talking about all the things they create. _How_ they create them. Each talk giving a small insight into the work required to do something I never knew was possible, or never quite understood the complexity of. It's another reminder to try and not just be a passive consumer of things.
I have to skip out early to get down to the US Consulate as they're hosting an event with [Andrew Hyde](http://andrewhy.de/). The Q&A part of the session starts and he gets the local version of the question I'm sure he gets everywhere he goes, "So what do you love about Melbourne?". He assures the crowd he actually means it this time, he loves Melbourne, it's _really_ one of his favourite places. Like he totally means it. He buys some extra cred for naming another city that would make the list (well done Beirut!). It's a great place to live. To visit. To start a business. Hrm, maybe he just has to say all this because LaunchVic are sponsoring the event. But then he says something that really feels like it zings. I'm somewhat paraphrasing, but it was:
> "You just f@#$!%& love the craft here. You love the craftperson. If you're at a restaurant you're not just eating the food. You're there for the chef. You can see the kitchen!"
> --- Something along the lines of what [Andrew Hyde](https://twitter.com/unicorn) said
I think back to the last big event I attended in Melbourne. Where TEDxMelbourne managed to have some really incredible people provide the catering. How I was a bumbling mess of embarrassment because my favourite chef personally handed me my lunch that day. How I blurted out a rushed appreciation for everything he's done over the past 20 years, and then skulked away like I was 7 years old.
This isn't just a city where you'll discuss whether your favourite coffee is a flat white, "magic", or a ristretto. It's probably situational. So you might not even have a favorite coffee place, it could vary depending on what type of coffee you want. You might even have a preferred barista for each. You could know which lanes attract the types of street art you like. If you do you probably have a favourite artist. Or maybe you'll queue up for 4 hours on a Saturday morning for the [best croissants in the world](http://www.nytimes.com/2016/04/11/t-magazine/food/lune-croissanterie-melbourne-croissants.html). Or join the hordes who wanted to get into Hawker Hall when it opened because that team had done such a great job with Chin Chin, Kong, and Baby before it. Or you're at the night markets that bring together all this vibrancy and creativity into one place so you can experience bite-sized portions of the best of it. Perhaps you immerse yourself in White Night, where the graffiti walls are replaced with a light-show that gives yet another perspective of all of the ornate artistry in the buildings we walk past each day.
Patricia Coffee, Melbourne. Photo credit: Tourism Victoria
We live in a ridiculously blessed and fortunate country. And in a vibrant city within it. That fortune and circumstance gives us the time to pause, take in, and appreciate so much of what is around us. The irony is I don't think I fully appreciated how much that just naturally happens across an otherwise ordinary day.
There's a very special ecosystem here. One with a beautiful intersection of people who have passion for making things people value, and a ready audience to show their appreciation for those things.
## The Australian Startup Scene
Agile. Innovation. Disruption. If you closed your eyes it'd be easy to think Australian politicians
were nothing more than a bunch of post-adolescent CS-grads pitching to their first VC. If you
can wade through the buzzword soup though there's the occasional piece of substance. The idea that
maybe, this time, they might actually "get it".
I was recently asked what the local ecosystem is like for trying to start a company. Usually when someone
asks "what's the startup scene like in X?" what they really mean is "how does it compare to The Valley?"
(or the Bay Area in a more recent, broader definition). It's different. In mostly good ways.
This is a tale in three parts.
## Act 1 - Our protagonist leaves the Bay Area
After several years in the UK the eternally grey skies of London had worn me down. As had the
endless search for "the next client" that any consultant/freelancer knows too well. We weren't
ready to come home, nor did we have a new destination in mind.
Instead I decided to let my career guide me.
I wrote a list of 3 companies. They were the companies whose products most excited me at the time.
By co-incidence they were all based in San Francisco. So I guess that's where we need to move to then!
As someone who'd been writing software for as long as he could remember it seemed like a right of passage
I had to take at some point.
I picked one of the companies and sent an email. A day or so later I got a reply from the CTO to
schedule a call. Several calls and a flight to SF later and I had my offer letter. I never emailed
either of the other companies. In January 2011 I started working at Heroku.
### Solving important problems
> OH: SF tech culture is focused on solving one problem: What is my mother no longer doing for me?
> --- [Aziz Shamim](https://twitter.com/azizshamim/status/595285234880491521)
There's a lot of talk of disrupting old industries. Of new unicorn startups, that will inevitably change the
landscape forever. Except there's also too much truth in that glib tweet above. If my mother stops taking care of me,
[how will I get to soccer practice](https://www.uber.com/)? [Who'll wash my clothes](https://www.getwashio.com/)?
[Who will cook me dinner](https://www.sprig.com/)? [Clean the house](https://iamexec.com/)? [Choose my clothes for me](https://www.trunkclub.com/)?
[Pack my suitcase](http://www.dufl.com/)? Do whatever I need because [I can't function as an adult](https://getmagicnow.com/)?
They say necessity is the mother of all invention. Want to know a little secret about why SF has become the epicenter
of this hot disruption? It's not because of the abundance of VC investment. It's not because of the congregation of
our best and brightest minds.
It's because so much basic infrastructure is broken compared to other major developed cities that it _needed_ to
be created by private companies. And also because mommy wouldn't do it any more ;)
No Londoner in their right mind was sitting there thinking "I need an app on my phone to procure a private limousine".
Outside of that time of the year when offices have their Christmas parties, grabbing a black cab is usually a case of
waving your arm or standing on the side of the road for 2 minutes. Plus for all their complaining the tube and bus
networks are actually really good. Or if the weather is nice you can grab a bike (for free) and ride it for 30mins
to where you need to be. I suspect New Yorkers had a similar experience. Anybody who experienced taxis in SF pre-Uber
has no doubt as to why it was created.
### Stay healthy
Many Americans, especially their politicians, are strangely satisfied with their healthcare system. This is despite it
being the [most expensive in the world](http://www.pbs.org/newshour/rundown/health-costs-how-the-us-compares-with-other-countries/)
(2.5x the OECD average, and over 17% of GDP) and by some measures being [one of the worst](http://www.forbes.com/sites/danmunro/2014/06/16/u-s-healthcare-ranked-dead-last-compared-to-10-other-countries/#56d7c6961b96).
I've heard first-hand the tales of people who were almost bankrupted from giving birth to a child. Out of pocket thousands
from a "routine checkup" after a minor fall from their bike on the way to work. Having to decide which pre-natal blood
tests they could actually afford because their insurer wouldn't cover them and they couldn't afford $700 per draw
for all of them (that $700 was _just_ for the 30secs it took to draw blood. The actual testing cost extra!).
Why does any of this matter?
One of the rare opportunities I had running the Heroku Add-ons Ecosystem was helping over 100 startups launch and
sell their product. I've witnessed first hand the change in attitude that happens to a person, to their company, once all
the bills are paid. When there's enough money coming in to know you've got everything covered everything is easier. People
stop being reactive. They make smarter decisions.
Starting a company is fraught with risk. What happens when your own health is coupled to that risk? How do you pay for
healthcare if your company fails? What happens if you suffer a major illness in the early stages and your company fails _because_
you're unable to work? No company, and no healthcare... who pays those exorbitant bills now?
This is a massive unconscious distraction. Some will argue it makes people hungry. It promotes a "failure is
not an option attitude". The latter is probably true. But when I've seen that play out at a corporate level "failure is not
an option" equals "we'll take the safe and middle of the road option". People become even _more risk adverse_. The lack
of a true safety net here for people is the complete antithesis of promoting genuine risk-taking innovation.
### You only get one shot, one opportunity…
I was blessed to join a company that actually cared about people. Many of the friends I met in SF weren't as fortunate.
"Just one more release, and then the big launch", but the launch came and went without the fanfare and user adoption required
to justify all of the funding. And so the hamster wheel kept spinning. As did the 18 hour days. "We're only doing these
crazy hours while starting. Once we _really_ launch we can take a break". It never happens. The people. The company. The handful
of users. The VCs. Eventually something flames out, and then everything else does too.
Which is how the game works. The most likely outcome is that the company you're working for _won't_ make it. It took me a while
to realise lots of people not just understand that they play the game to help stack the odds in their favour.
You need to diversify your risk. Spread your bets. And that means waiting for that first vesting cliff, taking your handful of
equity, and then promptly moving on. Equity becomes this toxic factor that encourages anybody who understands
the mechanics of the game to move on as quickly as they can. What's more to fully maximise your return you need to [forward exercise](https://gigaom.com/2011/06/05/5-mistakes-you-cant-afford-to-make-with-stock-options/)
all of it upfront. The entire game is stacked in favour of the fortunate few who have the luxury of changing jobs
every 12 months, accepting a below market rate in return for equity, but with enough liquidity to purchase all that equity the
day they start each job.
WTF?! :astonished:
### Failure. Success. It's all the same.
I've heard one of the differentiating virtues of "The Valley" is the celebration of failure. And no doubt, there are things
applauded there as success that I think by most other measures people would have a hard time being called a "success". The acqui-hire
of a team that spent 2 years building a product that is now being immediately shutdown. The acquisition of an
overnight success by an out-of-touch media organisation struggling to remain relevant. The IPO of a company still struggling
to find a business model so that the "smart money" can cash out while offloading the debt onto the general public.
There are positive aspects to this though. I know in many places that being at the helm of a sinking ship means nobody will
let you be captain again. Sometimes there are market forces, timing, or any other number of legitimate reasons why you weren't successful.
The statistics say that failure is the most likely outcome. So it seems unfair that individuals be punished indefinitely for
achieving the median result.
### Fit the mould
White? Male? Decidedly middle to upper class? You'll do fine. Many of the best colleagues I worked with were called
Matt.
I realise the irony and hypocrisy of someone fitting that description complaining about the challenges of day-to-day life in SF.
It was upsetting walking to work each day. Past the make-shift shanty towns of homeless people under the I-80. The guy out
the front of El Farolito who always seemed high on something, abusing anyone who walked past. Until he saw me and then he was
always sober and happy because he knew I was good for whatever change I had on me. His ranting and aggression suddenly seemed
like an act. Why was this his way of dealing with the world around him? Where would those people under the I-80 go when the cops
eventually told them to move on?
It didn't matter. We're all too busy helping people to book private chauffeurs or rent out an inflatable bed in their living room for
$1,000/night while Google IO is on.
For all of the money flying about. For all of the disruption. The innovation. The making the world a better place. It's all helping
the same people over and over again. There's only so much spare change I could hand out each day. It wasn't making enough of a difference.
And the problem felt too big to even to know how or where to try and tackle it.
It ate away at me. The happiness from knowing that my (hefty) rent was providing a good income to my lovely Nicaraguan landlords
was offset by the fact people like me priced their children out of living near their family. 3 generations growing up in our house,
but now unable to live in it because I was willing to pay far more than they earned.
And that's possibly the shittiest part of all of this. They grew up in this city. Their parents grew up in this city. And their grandparents
immigrated all those years ago to provide a life for them in a romantic town that is always undergoing a gold rush of some
sort. Except this time they're not invited. The party is happening and their name isn't on the list. In part because they're
not called Matt. And now some punk from the other side of the world has kicked them out of the family home.
## Act 2 - Destined for familiar lands
Australia is often called "the lucky country". We've had it ridiculously good (excluding Indigenous Australians :disappointed:, I'll come to
that too). When white settlement established us as a British penal colony they didn't just set us up for a wealth of original
jokes from foreigners. They gifted us a democratic system of government. We've never had to suffer the trials and tribulations
of most young countries. The civil wars. The instability.
To this day most of Australia remains immune to most of the real problems the rest of the world faces.
### The startup scene that never was
When we decided to move home to raise our family I'd resigned myself to the fact I'd disappear into tech and product
obscurity. I'd probably have to take a 9-5 job at some big bank where I'd while away the hours writing scripts to correct
the broken escaping in CSV exports. I certainly wouldn't be working for or on a "startup". But such is the price you
pay for dependable healthcare, good coffee, and life by the beach.
Imagine my surprise when I got back and dragged my sorry arse to the local [Startup Victoria](https://startupvictoria.com.au/) meetup.
Over 400 people all chatting away about the startups they were working on or dreaming about. There to listen to some of the country's brightest minds
from the CSIRO talk about how they're printing solar cells from stock-standard inkjet printers. How they're one of the first to be
able to 3D print using titanium. Their Nano Fab facility the general public can just drop in and use. And then the next month it was
a similar crowd to hear Steve Blank & Jerry Engel talk. And Dave McClure was the following month. _Hundreds_ of people, month after month,
excited about this stuff. I've seen conferences struggle to get this kind of patronage. And this was just a local meetup.
I talked to people. I pinged random founders at interesting companies via whatever social network I could find them on. I asked if I
could talk to them about what they're doing, what I've been doing, what I'm thinking of doing. Every single one of them said yes and
spared me time for a coffee. _Every one_. Everybody has been so ridiculously generous with their time and genuinely helpful.
There was a common thread with most of these "startups" though. Most of them wouldn't fit a Steve Blank-esque definition of a
startup. They had repeatable and scalable business models. They were making money. Most of them were profitable, handsomely so in
some cases. And most of them had achieved it early. Not after 10 years and 5 rounds of financing. Not after 5 years. Most were
break-even profitable within 12-18 months. Some were bootstrapped and profitable almost immediately, either spinning out of existing
companies or consultancy work.
If anything Australia's primary failure here is in not celebrating it's successes more. Realestate.com.au, Seek.com.au, and CarSales
are all publicly traded tech companies. All of them with >$1B public market valuations. Atlassian recently IPO'd with a $6B
market cap. Campaign Monitor is rumored to have raised back in 2014 at around a $1B valuation. Companies like Aconex are doing
great but aren't selling their software in an interesting enough sector to get regular press (current public market cap of ~$800M).
There's so-called unicorns everywhere here. The difference is they're actually earning their valuation, by making actual money. I
don't know if it's the lack of a frothy VC sector or cultural differences, maybe it's both. But making money is in the DNA
of companies here. They don't need to keep raising to stay alive. They're not living round to round wondering if they're going
to be able to keep the lights on.
The result is companies like Campaign Monitor, 99Designs, and Canva. That prove out legitimate businesses and then take on money
to repeat that success at an epic scale. As an approach that makes far more sense to me. More sense than [throwing millions at a
phone app and worrying about how it will make money later](http://mashable.com/2012/10/17/color-shuts-down/#OFrk.4Tf_qqF).
### Redeploying capital
We've historically been a pretty simple country from an economic perspective. Commercially we dig things up and send them overseas
or we run banks. Personally we buy houses. These are all well understood businesses with either low risk or risks we understand
and can mostly manage. They've also been incredibly prosperous for us all over the past 30 years.
When would-be-founders start talking about the lack of local capital I can't help but shake my head. We're actually [the wealthiest
individuals in the world](https://en.wikipedia.org/wiki/List_of_countries_by_wealth_per_adult) (look at that debt tho :fearful:). It's
just most of us have better things to invest our money in than your risky startup. Like a property market that's been returning
double-digit percentage return year on year for ages now, plus the rental income. Or our big banks that were immune to the financial
crisis that gripped the rest of the world, kept growing, and kept paying out hefty dividends. It's pretty hard to put a compelling case
forward for most startups that they're likely to beat long-term successful businesses.
Sorry, you want how much of my money for this napkin sketch that's not yet even a company?
It's not that there is no money. It's just that it's being invested elsewhere. Bear in mind that the Australian superannuation
system (British readers think pension fund. US readers it's our 401K) means that all working Australians have a government mandated
minimum of 9.5% of their gross salary invested somewhere.
Think about that for a moment. Every working Australian. Of all ages. Has 9.5% of their gross salary each and every week
that they _have_ to invest somewhere. That works out to be [$2 **trillion**](https://www.superannuation.asn.au/resources/superannuation-statistics)
of assets under management. And it's not from investors in a fund that expect a return within 5-10 years. These investors are
basically forbidden from touching that money until they're 65 years of age. They've an investment horizon that spans multiple decades.
We can take really long bets.
The problem for people with money is that even if they are motivated to invest there can often be very real and significant tax
implications in liquidating one asset to invest in another (i.e., you'll give up to half of it to the tax office). And so there's
little incentive to do it.
Enter the tax incentives! [20% tax offsets for angels investing up to $200k](http://www.innovation.gov.au/page/tax-incentives-investors), so
you can reduce tax you'd have to pay on income elsewhere. Plus any gain you make on that investment is tax free so long as you
hold the investment for 3 years. Suddenly moving some money out of property and resource markets that might be on the wane
looks much more appealing.
Don't have the cash to invest, but you're actually thinking of starting your company here? Doing something new? Taking an
approach of experimentation to find out what works? Chances are you'll qualify for the [R&D Tax Incentive](http://www.randdsnapshot.business.gov.au/Pages/default.aspx#).
Have 45% of your R&D costs (including staff wages) given back to you as a tax offset.
Now go forth and spend that money on growing your business. I think most companies in The Valley would scream at the chance of having
almost half of their costs returned to them each year rather than having to raise more money. Unfortunately you need to
be turning a profit for a tax offset to be of any value.
That sneaky tax office. Encouraging companies to be profitable.
### Government that gets it
I'm pretty skeptical of government involvement in most things. They've a pretty mixed record here on their ability to actually
implement large scale and long-term initiatives. And in the current media cycle it's easy to get lost in the same old platitudes
about engaging stakeholders and broadening discussions to every day Australians. But this time it's different, right?
For a start the country is currently run by a [former investment banker](https://en.wikipedia.org/wiki/Malcolm_Turnbull#Professional_career),
who then went on to be chairman of one of the country's largest ISPs. Which was acquired by a US telco at the height of the
first dotcom bubble. He's been to the coal face. He's cashed out in the highs and seen the lows. There's probably very few
people in the country that have more experience of tech companies at that scale and success than him. It still feels weird
admitting that of a Prime Minister.
Then he's sent out his "assistant minister for innovation", Wyatt Roy. Who then got a bunch of entrepreneurs to [suggest policy
ideas](http://www.policyhack.com.au/) on a public forum where people could vote on them. I had low expectations, the comments on
internet forums being what they are. But then a group of those entrepreneurs got together one day in Sydney and iterated on
the best suggestions to see what they could come up with. It felt refreshing even though I wasn't there. Implied in all of this
seemed to be an admission that "we're not going to give you piles of government cash to do these things, find different ways
to make it happen". And as a result the day produced a bunch of actually actionable projects that primarily just require
peoples time.
Single day, government-led working groups, that produced actionable outcomes. What a time to be alive.
There's still lots of policy decisions in other areas I'm pretty unhappy with. But in this particular small part of the world they're
making some worthwhile decisions.
Most inspiring of all is the admission that we shouldn't be looking to copy models that were successful elsewhere, a view that
seems surprisingly uncommon. We should play to our strengths. We've a strong financial sector and access to a considerable amount
of capital. We're the one of the largest developed countries in the Asia-Pacific region, the largest English-speaking one. We're uniquely
placed with access to easy access to large established markets and the 2 billion neighbours who are rapidly entering the middle classes.
We've a wealth of talent. A lot of it being lured overseas, but much of it wanting to (like me) come home at some point. And we've a
quality of life that is, rightfully, [the envy of the world](http://www.abc.net.au/news/2015-08-18/melbourne-named-worlds-most-liveable-city-again/6705274).
### Actual balance
Ping pong tables, bringing your dog to work, open bars, and free lunches. It seems people are easily lured by perks and
can confuse them with actual work/life balance. You can't replace that balance with the ability to actually live at work.
There's very few companies I've seen in California that get this right. They try to turn the company into one big family,
and accidentally ostracise those who'd rather spend time with their actual family. Or promote "unlimited vacations" but don't
mandate any minimums. Which means huge numbers of employees take zero vacation time until they're burnt out. It turns out not
everyone has the confidence or job security to announce they're taking 3 weeks off.
There's a certain modest amount disrespect of authority figures in Australia. Within an organisation I've usually seen
that translate to people having fewer hesitations in saying no to a boss. Or at least complaining to them loudly about
it. No working on a weekend is not fine. No doing 3x 18 hour days back-to-back is not fine. No. No. No.
Also a lot of this stuff is enshrined in legislation to protect basic workers rights.
### Genuine care
One of the most exciting aspects of engaging with the local scene was the effort being expended on genuine human
causes. One of the first cafés I went to for a meeting was [Kinfolk](http://kinfolk.org.au/), where with each purchase
you vote on which project their profits should be distributed to. The walls were lined with other companies doing other
similar and amazing work.
Which is where I learnt about [STREAT](https://www.streat.com.au/), who provide a sustainable pathway for homeless youths
to get off the streets and into a job so they can get back on their feet. I got them to cater a meetup we ran recently, and
with the proceeds they'll be able to provide 12 hours of job training to someone so they can
stop living on a bench or in a doorway somewhere. With all the meetups happening every
night in SF, imagine the impact such an initiative there could make. And all it takes is for people to stop eating shitty pizza.
Another friend has the [Asylum Seeker Resource Centre](http://www.asrc.org.au/) cater his event. Helping people who have
been displaced from their home country stay fed and find work here.
Then there's things like [The Slow School of Business](http://slowschool.com.au/) that's trying to encourage people to
build genuine purpose driven companies. That it's not always about competition and winning at all costs. Or education
initiatives like [Code the Future](http://www.codefuture.org/) that pair schools that need technical help up with technologists
that have the time. As a result every Monday I spend the afternoon teaching grade 5 & 6 kids how to code in Scratch and
build things with Arduinos.
And most impressively there's conferences like [Above All Human](http://aboveallhuman.co/). A wonderfully run event that
challenges the 1,000 attendees to think of the ethical implications of what they're working on, reminds us just how insignificant
our contributions really are on a cosmic scale, but that despite of all of that we're still able to have meaningful and positive
impact on each others lives.
### Tractable problems
> Australia - no recession in almost 25 years, but five prime ministers in the past five. Multi-car pile-up in good driving conditions.
> --- [Nick Bryant](https://twitter.com/nickbryantny/status/643402261616529408)
That isn't to say things here are perfect. We have an incredibly shameful history of how we've displaced and treated our indigenous
people. They continue to [infant mortality rates double the rest of the population](http://www.australianstogether.org.au/stories/detail/the-gap-indigenous-disadvantage-in-australia).
Live a decade less. And are up to 5 times more likely to die of preventable causes. And there's long standing systemic reasons why much of this is the case. We're dealing
with a significant rise in social problems associated with [increased methamphetamine use](http://www.news.com.au/national/inside-australias-drug-epidemic-how-ice-is-tearing-our-country-apart/news-story/e8e525490732c7aced328d001481df05).
But we also don't have majority-minorities being marginalised by institutionalised power structures. We have problems that feel like they're actually
solvable in the medium-term.
Part of our problem in being ["the lucky country"](https://en.wikipedia.org/wiki/The_Lucky_Country) is that we've not really had to work for
our good fortune. We've lucked into living on top of lots of valuable dirt. We were gifted with a functioning system of government. But we also
have a history of actually coming together in a bi-partisan way to do what's necessary when we have to. We've sailed through a number of global
recessions largely unscathed, thanks in part to the reforms put in place by the Hawke/Keating administration and continued later by Howard.
And so I'm genuinely excited to see initiatives like the [IDX Hub](http://ncie-idx.tumblr.com/) and [IDX Innovators Lab](http://idx.org.au/get-involved/innovators-lab)
help broaden the access to these opportunities. [AIME](https://aimementoring.com/) are helping close the gap on graduation rates. And I've already
mentioned other efforts like Code The Future, STREAT, and ASRC. These aren't unique efforts. They're things I've become involved in just through my
regular day-to-day life. By buying coffee. Having a meeting. Eating food. Doing my work.
Most of the problems we face only need time and energy to solve.
And because we're not working 18 hour days. Not staring at the end of a runway fast approaching. Trying to avoid a down-round with the
next inevitable raise. We've actually got the time and energy to commit to these causes. To find a way to have meaning and purpose outside
of our day jobs.
### Apply some heat
At the most recent installment of Above All Human, Leni Mayo took the stage to open the event. He spoke nostalgically of having an audience
with [Alan Kay](https://en.wikipedia.org/wiki/Alan_Kay), who at the time spoke of how our fixation on construction and engineering analogies
limits our thinking.
Leni challenged us to think of a dish of amino-acids, a primordial soup. The amino-acids bump into each other, and very occasionally create
something amazing as the result. And that all we needed to do was to apply some more heat, to speed up the process, and watch the results.
Just imagine an ecosystem with access to $2 trillion in capital. With an additional 9.5% of gross earnings pouring in each week. With a corporate
DNA that has always prioritised profitable and long-term sustainable businesses. With access to both massive English-speaking markets and its 2 billion
neighbours. With a social welfare system that enables genuine risk-taking. With a healthier and happier work culture than most. In a location that is simply stunning.
I want to turn up the heat.
## Act 3 - Where sustainable businesses win in the end
It's being written as we speak… check back in 10–15 years.
## ABCs of Startup Financing
Aussies Be Coming. Got an idea for a new startup? Need to maximise your runway
to make it happen? Here's why Australia is possibly the best place to do it.
A number of people reached out after my discussion on [the differences between
the Australian and Bay Area startup scenes](http://glenngillen.com/thoughts/australian-startup-ecosystem),
pointing out that I'd actually left out a lot of important detail on the
financing front. That in many cases things were even better than I made out.
So meet Kylie.
Kylie is a fictional character to support a narrative construct. She's also
a budding startup founder. She's building a infrastructure platform that is
going to change the way developers think about security and integrating
services.
## The pre-seed stage
> Back in my day we paid $40k _per server_. And we had to wait 6 weeks
> for it to arrive. And then we had to plug it in, ourselves, in a
> cupboard somewhere.
And that's the way we liked it.
Kylie did some research on Google and LinkedIn, identified the type
of firms that invest in the big ideas she had. She dropped an email
to a couple of VCs and asked if she could meet them.
They said yes! Who knew it would be this easy?!
They met in a coffeeshop and she delivered her really rough pitch. The VC
listened intently. And then gave her some really good feedback. The challenges
they saw. The previous attempts they'd seen fail. The sectors where this
idea, with some tweaking, might get some traction.
And then came the gut punch.
"Come back to me when you've got it working". They talked a little more. The
VC admitted they didn't invest in ideas. That products like Heroku had reduced
the infrastructure cost for getting a prototype into product to virtually zero.
She didn't need their money. She needed to find the time to actually build it.
She setup a company. She felt like making it "real" would help keep her honest.
### Sweat equity
The VC was right. So she found an hour or two in the morning to hustle. Set up
calls/coffee/whatever with potential customers. Then she went to work. She'd get
home at night and spend 2-3 hours coding. Because of the disjointed nature
of the work she had to be even more organised. But that was fine. Thankfully
that regular planning each morning meant the evenings were hyper focussed.
It was getting done.
And then each weekend, two whole days to work on it uninterrupted! Oh the luxury!
She didn't need to build the _whole_ product. Just enough to demo. Enough to maybe
close that first sale.
After 3 weeks she was mostly there.
### Consultancy
Kylie had a little bit saved in the bank. A few months to cover all of her living
expenses, longer if she cut back on her recent Tim Tam addiction (hey they we're
$1.89 a packet, don't judge me! You're not my real mum!).
She bit the bullet. Handed in her notice at her day-to-day job. She had to give
a month, so she still had about 4 weeks of income while she tried to line up
something else.
She hustled. Hard. She met with lots of companies who she suspected had the
problem she was trying to solve. Many of them did. Most of them were trying to
build some hacky workaround to buy them more time. So they didn't have to address
it right now.
She asked if she could come in on a contract to build that workaround for them.
They said yes.
They scoped out the work, agreed a rate, and when she left her job she walked
straight into a new one. Approximately 6 weeks of effort, at a much higher rate
than her previous salary.
She got to experience first hand the problems the customer faced. The competing
priorities. The compliance requirements. The deployment work flow the solution
had to fit into.
It was the type of customer research and feedback you just can't buy. And she
had multi-billion dollar company _paying her_ for the privilege.
Each night she came home and worked on her startup. The days lessons feeding
back into the product design. Making it better. More polished. More fit for
purpose.
The contract ended. She moved on to one of the other potential customers that
said yes to her offer. The arrangement was the same.
But the cash-flow was like sugar water. The company now had money in the bank,
but somehow 6 months had gone past. Where did all that time go?! It was time
to cut the cord and let the product try and find it's way in the world.
Maybe just one more client? Another month of contract rates in the bank? Things
would be easier. Less risky then. Right?
Kylie had a good friend who talked some sense into her. One more month of earnings
wasn't going to make or break it. It wasn't a significant enough impact on anything.
And she was still really just selling her time, not a product. It wasn't scalable.
If money was what she needed then she needed much more than one more gig. Meanwhile
each gig ran the risk of the opportunity moving past her.
### Family & Friends round
Kylie had a rich uncle (of course she did, she lived in the [wealthiest country in the
world](/thoughts/australian-startup-ecosystem#redeploying-capital). She invited him
over to talk about her plans.
He was impressed at how she'd approached it. Bootstrapping the business through consultancy.
The breadth and depth of feedback she'd gathered. The way she'd been able to incorporate
that into the product.
But then came the part that made he feel _really_ uncomfortable. She said she needed money. More
than she had. And that's why she'd asked him to come over. Her stomach was in a knot. She felt
sick. She new this would be a feeling she'd have to get used to. Meanwhile he just stared at her blankly, unflinching.
"Sure. How's $200K? I want 10% of the company."
What?! Yes! Of course!
The timing was perfect for her uncle. He had a property he was trying to sell, and some mining
stocks he'd made a nice profit on but were suddenly looking shaky. He was about to liquidate
assets and needed somewhere to re-invest the money.
And thanks to [recent tax changes for angel investors](/thoughts/australian-startup-ecosystem#redeploying-capital) he'd
get a $40K tax offset for investing in her business, which would help reduce his tax elsewhere.
Those changes didn't come into effect until July though. And he had to wait for settlement on the
sale of his house anyway. So it'd be a few months before Kylie would get the money.
### More traditional sources
Most of Kylie's friends were up to their eyeballs in debt. She'd never been too concerned about
new flat screen TVs or limited edition retro Air Jordans. So she had a credit card, with credit to
burn.
While she was waiting for uncle to transfer the money she filled the immediate gap with a card.
Servers by the hour from Amazon Web Services, they had pretty much everything she needed. A logo
and branding from 99Designs. She found an illustrator who's work she loved on Envato and worked
directly with them on content for the website. To make it look professional.
A week later she was done, for now. If someone searched for her or the company they didn't get
a parked domain page.
It felt good. But she also didn't want to depend on the credit card. She had cash in the bank
from the consulting, so she had no problem paying it off. But she also had no reason to be paying
20%p.a. in interest if she did need the cash.
She was going into her bank anyway that afternoon. She spoke to someone there about getting a business
loan. They looked at her history. The business income. They suggested a line of credit. She could
access up to $50K at just under 8%p.a. if and when she needed it. It'd cost her $400/year for
the privilege.
She decided to think on it for a while. It was good to know it was an option though.
## The seed stage
All the previous efforts had worked. Kylie sold to her first customer (one of the potential consulting
roles that she never got around to). And then her second. And third. One of the companies she consulted
to was thinking about buying the product too so that they didn't have to manage that piece of infrastructure
themselves.
Things were going great.
The extra money meant she'd even been contracting out development work to some local devs to speed things
along. But there's now so much to do. She's trying to juggle sales, product management, and development.
As well as managing a team of loosely related contractors.
She wanted to grow the team. Make some of these contractors permanent. She needed more cashflow to support
that though. She'd get that if she could close more deals. But she didn't have enough hours in the day
because she was doing at least three jobs. And she couldn't afford to fill one of those jobs with a full-time
person because, well she needed more sales to justify it.
:chicken: and :hatching_chick: problems.
Time to go back and speak to that VC. Or is it?
### R&D Tax grant
Kylie has always been organised. She had to be in those early days where she was juggling a full-time job
and starting her company. Everything has always been very methodical.
- What do I think the problem is?
- What are my assumptions?
- How do I either validate or invalidate that hypothesis and move on?
We're approaching the end of financial year anyway so Kylie was speaking to her accountant about her situation.
He mentioned that the government offers an [R&D tax incentive](http://www.business.gov.au/grants-and-assistance/innovation-rd/RD-TaxIncentive/Pages/default.aspx) and
that he thinks she might qualify.
They do the numbers. She's been paying herself a small wage since the company started. And then there's the
contractors she's been using. The odd expenses here and there to support the activities. They go back over all of
the story cards for the past year of work. Work out which bits qualify for R&D, which bits are sales & marketing, etc.
They work out who spent what time on each.
Eventually they work out that there is $150K in R&D costs this year. And that Kylie's company is eligible for a
45% rebate on those expenses.
She's been pouring all the money she can into growing the business. She's not actually going to make a profit this year (or
a _very_ slim one if she does). So that rebate comes back as cash.
That's $67.5K back from the government.
Enough to bring on a junior developer or sales person to take some of the work off her plate. She has a decision to make.
### Accelerating Commercialisation
The accountant wonders if she had to make a decision at all. "Why not hire both?", he says. Hah! An accountant who is
actively encouraging me to spend more money than I have! Who is this guy?!
He says he helped another client apply for the [Accelerating Commercialisation Grant](http://www.business.gov.au/advice-and-support/EIP/Accelerating-Commercialisation/Pages/default.aspx)
. They'll agree to split the bill on eligible expenses, up to $1M. And the one of the motivators for growing the dev
team is to build out a new product and revenue stream based on an opportunity she's seen.
There's a couple of large deals closing straight after the end of the financial year. The $200K from her uncle should finally
land. They submit the expression of interest. Time passes. They speak with people at AusIndustry. Milestones are set and
agreed to.
They've been given an extra $400K to spend over the next 2 years.
### Closing that seed round
What a year! $400K from the AC grant. $67K from R&D grant. $200K from her rich uncle. It's built a team. A product.
Some serious traction.
Let's turn the heat up on this thing.
Kylie decides to finally go back to that VC she spoke to over a year ago now. They're seriously impressed with the progress
she's made. The team she's built. This is an entirely different discussion to the previous one. They _obviously_ want
to give her money now.
It's just a question of how much, and at what valuation.
She reaches out to a few more. [Blackbird Ventures](http://blackbird.vc/), [Square Peg Capital](http://www.squarepegcap.com/),
[AirTree](http://airtree.vc/), [Rampersand](http://rampersand.vc/). They all get it. They all see the potential.
One of them leads the round, a couple of others join in to fill it.
She's now got a fantastic group of investors who can help grow the product into the places she needs help accessing. And
an extra $1M to do it with.
## The post-seed stage
The investors were all excited about the global potential of the product. That's always been part of the plan. But now
that Kylie and the team have proven it out locally. It's time to take that cash injection and grow internationally.
### Export Market Development Grants
That accountant is worth his weight on gold. As part of her quarterly catch-up she tells him about the success of the
recently opened office in San Francisco. He asks her to identify all of the costs associated with that international
expansion. The flights to and from. The marketing visits. The local consultants you'd hired to help bootstrap
the required local market expertise.
He trawls through all the receipts. He works out there's $80K in eligible expenses associated with growing
the export market for the business.
Kylie's company is eligible for $40K in [EMDG rebates](http://www.austrade.gov.au/Australian/Export/Export-Grants/What-is-EMDG).
## The infinite runway
The various government programs in place have worked. They've said "if you, or someone else, is willing to commit money
to growing this business into something successful we're going to meet you half way". And it's mostly no strings attached.
No equity stakes by the government. They trust they'll get their money back in the long-run via company tax, GST, and
income tax on your well paid local employees.
And they will. Because the company is profitable.
But even then they'll continue to offer you rebates for qualified R&D activities as you try and validate and grow into
new markets.
And that's what Kylie continues to do.
But because she's not had to raise round after round. Because she's basically gone into partnership with government to
launch the business. She still owns the majority of her company. Which is a [damn sight better than the outcome for
most founders](http://avc.com/2009/02/founder-dilution-how-much-is-normal/) in more typical places to start a tech
company.
> Special thanks to [Rayn Ong](https://medium.com/@rayn_ong/five-things-to-look-for-in-a-good-aussie-startup-e8bfa70e42b9),
> [Matt Allen](https://twitter.com/mattallen), [Doug English](https://twitter.com/douglasenglish), and [Tim Lucas](http://toolmantim.com/)
> for pointing out the bits I missed, making the numbers accurate/realistic, and generally providing great feedback.
## Always take the interview
Many years ago, I think it was probably around 2012 or 2013, I was living in San Francisco and it seemed like "Walmart Labs"
was suddenly appearing everywhere. I was already familiar with them to some extent, they were one of our largest customers at
the startup I was working at, but now their name was appearing out in the wild.
Keep in mind, this isn't "Walmart". The was a _team within_ Walmart! They had no product to sell you. No store for you to visit.
I assume in response to how dominant Amazon was becoming they had decided to invest in their digital presence, and that meant trying
to play catch up. In the way only companies like Walmart can: by spending lots of money to suddenly seem ubiquitous. Or ubiquitous
within the bubble that is the Bay Area at least. When I went to the local street food park for lunch there were people wearing Walmart
Labs swag, handing out small flyers or business cards. Billboards started appearing advertising them. Before long there were cars
driving around SoMa with those billboard trailers. Seemlingly everywhere, San Franscisco was fading into the familiar colors of Pantone
285C & 1235C.
Remember, Walmart Labs had no stores. No product. This was entirely in service of a hiring blitz! They wanted people to come work for
them and this is how they were getting on everyone's radar.
And then I got the phone call. Someone from Walmart Labs reached out with a fairly vague, "hey would you be up for a chat?". A very naive
me from a decade ago didn't bother to look them up and thought little of it, they were after all one of our largest customers, and so
of course I was available for a chat. I sent through my number and shortly after we're talking. It turns out it's someone from their
recruitment team and they wanted to know if I was potentially looking for a new job, "No, I'm quite happy where I am so apologies for wasting
your...", "No, it's not a waste of my time. Could I ask for 5 minutes of yours so you can hear me out first?"... "Ummm... yeah, ok I guess". What
followed was actually a pretty compelling pitch. Those 5 minutes shifted me from "absolutely no way, I already have the best job" to "huh,
they're actually more interesting than I gave them credit for. Probably still a no... but... maybe?". Much of the enjoyment of working
at a startup is the feeling of connection you have to the work you're doing, and the more direct impact it has on the company. When you're working for a
large company it's easy to feel like a tiny insiginifcant cog. To their credit, the Walmart recruitment team clearly knew the audiences
they were pitching to. Imagine working for a startup _inside_ one of the world's largest companies! You get the agency and autonomy you
want from a startup, but with the knowledge that only a 1% improvement to anything for a company the size of Walmart is enormous. Potentially more
impactful than most startups could dream of. It gave me a lot to think about.
The next morning I raised it with the CEO of the company I was working at (who was also my boss at the time). I didn't think I was special enough that I'd have been unique
in terms of outreach, they were probably reaching out to lots of our employees. My resolve to stay was strong, I wondered about others.
Should we be concerned? Did he have thoughts about what we should do about it? I know it was just business, but it still felt kinda
gross to think they might be intentionally targetting our team given they were also an important customer. Should we talk to them directly
about it? He only had one question for me, "how much was the offer?". I didn't know. My 5 minutes with the recruiter definitely went longer
than 5 minutes, and we spoke about the kinds of things they'd work on, culture, outcomes they were looking for, team/org structures, but at
the end of it all I'd still ultimately said I was happy where I was and I'd reach back out if anything changed. He then gave me what I
consider to be some of the best career advice I've ever received:
> Always take the interview!
He seemed almost disappointed in me! Annoyed that I'd let this potentially golden opportunity slip through my fingers. It was the exact
opposite of what I was expecting. Surely my loyalty was meant to be rewarded! Why am I getting chastised for _not_ pursuing a process that
might ultimately lead to me handing this guy my resignation letter?!
His point was that all too often people approach the job market, and their own career development, in a suboptimal way. From a pure
outcome maximisation perspective the best time to take the next big step is when you're at the absolute top of your game. When you're feeling
great, when things are going well, when you feel confident, when you don't _need_ to change jobs. The compounding impact of all of those
things puts you both in an incredible position to present yourself in the best light possible but also the strongest negotiating position.
Is the offer from the other prospective employer not enough to entice you and/or compensate for the risk of jumping into the unknown? Great!
You're already happy where you are and weren't looking for a change anyway! The worst-case scenario here is you get to recalibrate your market
value which is a valuable data point if/when you ever have salary negotiations with your current employer. The best-case is you accidentally
stumble upon a new job that you find even more exciting than your current one!
The other point he made was around practice and building a muscle for interviewing. Something I didn't appreciate until years later when I
actually started meaningfully interviewing and looking for a job ago. I'd spent years interviewing people as a hiring manager. I know how they
work. I know how they're structured. I know the types of questions I'm likely to get asked. I know. I know. I know. Knowing and doing are
two very different things. I was embarrassed at how badly I fumbled my first interview as a candidate after such a long break. Struggling to
think of relevant examples when put on the spot. Talking about the same thing wwaaaayyyy too much. Actually, is what I'm saying even answering
the question I got asked? OMG, what even was the question!? You'd have come away from that not only wondering if I'd ever had a real job, but
if I'd even spoken to a person before. It was an utter trainwreck.
And that was my manager's other point. Don't let the skill atrophy. Keep exercising it. If an opportunity presents, even if you don't want a job,
take the interview just for the exercise. Keep exercising that muscle of being able to speak articulately about yourself without feeling
embarrassed. Tweak and refine it, while you're confident, while things are going well. Then when you need to actually use it hopefully the muscle
memory kicks in and you so well conditioned the interviews seem easy. The mistake most people make, the mistake I made, is to wait. Wait until
circumstances change and you _need_ a job. Maybe the current one has become stale. Maybe you're burnt out. Maybe you've been moved into a new team
working on things that aren't exciting. Maybe you lots you're job and now you absolute have to find something ASAP. Whatever the reason, it's
almost always _the worst_ time to be trying to rebuild this muscle.
If you're ever fortunate enough to have someone reach out wanting to talk to you about a role, always take the interview.
## Investing in Australian Startups
I've previously covered some of the cultural and competitive advantages
would-be founders have in [starting a company in Australia](/thoughts/australian-startup-ecosystem).
Not to mention the [incredible access to capital](http://glenngillen.com/thoughts/abcs-of-startup-financing),
despite the lack of a Venture Capital industry the size of Northern California.
But what if the days of starting a company are long behind you? What if you'd rather
invest in the next generation of companies?
We've got you covered there too! None of the following is tax advice. The examples are
for illustrative purposes. Before investing speak to your financial advisor. Yadda yadda. You
know the drill.
## Got a large tax bill?
Sarah is a person of means. She's amassed a considerable property portfolio over the past 20
years, like many Australians. Her high-paying job combined with the positive rental yield on
her investment properties (she's had some of them for 20 years remember!) means that along
with large amounts of equity she also has a really large tax bill every year. She admits
these are good problems to have.
She's thinking of taking $1M of equity out of her property portfolio and investing it in
a handful of innovative startup companies.
She puts $200K into 5 companies, and in return each company grants her an equity stake.
That's not all though. The [recent amendments to the tax law](http://www.aph.gov.au/Parliamentary_Business/Bills_Legislation/Bills_Search_Results/Result?bId=r5648)
means she'll also get a $200K tax offset. Which means she can earn almost **$9K per week**
before she has to pay any tax.
You read that right! Sarah can invest $1M, and then the next $468K she earns is tax free.
## Need to diversify that portfolio?
Diversification of assets across multiple investment vehicles and asset classes is
basically the first thing they teach would-be financial planners. Weighting risk
and return with risk appetite. It's important to take the net return into consideration (that's financial planning 201 :wink:). After
operating costs, _after tax_. But what about if there's no tax?
You heard right! Tax free gains! Come and get them!
Sarah has a friend, Megan, who has also done well with her previous investments. The main
difference though is she's in the twilight of her career. Megan doesn't have a high 6-figure job.
Her investments aren't returning enough to retire on, yet. So she sells off a couple of investment
properties and spreads the profit across a number of funds and... you guessed it, some innovative
startups!
She's going to hold the startup investments for at least 12 months. Which means she qualifies for
the Capital Gains Tax exemption for those investments. Obviously they're all going to become unicorns
in the next 5 years. And so when there's a liquidity event and she gets the opportunity to see that
$200K turned into $5M, it's all tax-free. All of it!
No more buying lotto tickets and trying to pick the winner of the Melbourne Cup. There's a new
way to make millions tax-free.
## Got lots of friends with money?
Cathy is fortunate to be close with many of her old school friends, most of whom have been
incredibly successful. Captains of industry. Minor celebrities. She grabs coffee with a
few of them and floats the idea of pooling their resources to launch an investment fund.
All she needs is $10M and a plan and she qualifies as an [Early Stage Venture Capital Limited Partnership](http://www.business.gov.au/grants-and-assistance/venture-capital/esvclp/Pages/default.aspx),
(though she can raise as much as $200M!).
The ESVCLP works great for all of Cathy's friends. They lead complicated financial lives. And the
ESVCLP works a bit like a trust. It doesn't pay any tax directly. Any profits it makes pass straight
through to the investors. No "double taxation". And they fit into whatever existing (or new) structures
each individual investor wants.
The investors in Cathy's ESVCLP also get that tax-offset that Sarah got, but at half the rate (10% of
the amount invested vs 20%). So each of Cathy's 10 investors who invested $1M each will get a $100K
tax offset to apply to their tax return.
## Live outside Australia?
Hey hey hey! Have we got the perfect way for you to maximise you investment returns _and_ have
a legitimate excuse to deduct a trip or two to this country as a business expense.
Remember how I said Cathy's ESVCLP works a bit like a trust? I know they're not a common investment vehicle
in other countries so it's worth pointing out the benefit again: they pay no tax. Money passes
through them, untouched by government agencies and tax departments. The expectation is the tax is paid
by whatever person or entity ultimately receives the money.
That includes foreign entities. The Australian Government has decided that if you're willing to
invest your dollars to help kick start our companies, we're not going to punish you when you reap
the reward. That money leaves Australia tax-free, and you only need to pay whatever local taxes
you must on the gain. No double-taxation. No worrying about tax treaties.
## That all sounds great and... innovative.
I keep using that word. Does it mean what we think it means? Thankfully we've a handy [100-point innovation test](http://parlinfo.aph.gov.au/parlInfo/download/legislation/bills/r5648_first-reps/toc_pdf/16053b01.pdf;fileType=application%2Fpdf).
Schedule 1, Part 1, 360-45. It's okay, there's only 8 points. And you need to qualify for 2, maybe 3, of them to
pass the innovation test and qualify as an "innovative startup".
If a company passes the test, all of the benefits aforementioned are bestowed upon its new investors! :tada:
# Tying a bow in it
There's a lot to love with this setup. It's been influenced very heavily by the SEIS scheme in the UK which
has been quite successful by most accounts. The primary difference is you get a tax offset instead of a deduction.
The latter heavily skews the net benefit towards those on the highest marginal tax rates and can cause complications
with other benefits and programs that are assessed on net taxable income. And offset provides a fairer
net benefit to all recipients irrespective of their marginal rate.
A cynic might suggest the reason we've used an offset is because a large population of potential investors are massively
leveraged and are using our negative gearing concessions to reduce their total taxable income to close to $80K, and
thus wouldn't receive much or any benefit from further deductions. Hold your tongue! Tax reform that is attractive
property investors too?! How could you suggest such a thing?! :astonished:
I suspect that's part of the plan. As a nation we're hugely over-exposed and in debt in our domestic property market.
With the downturn in mining we need to both find alternative sectors to drive our economy and limit our exposure to
the inevitable rises in interest rates. Here's a scheme that provides an incentive for those who are sitting on
a gain in a potentially over-valued asset class to cash in some of those gains. Invest them in another vehicle.
Receive an immediate tax benefit for doing so. And avoid paying tax on a future gain on that investment. It's
win-win-win-win.
Founders now have even more access to capital. Friends & family rounds of a reasonable size are completely feasible.
ESVCLPs are springing up all over the place, and have funds they need to commit somewhere. [I don't believe lack
of access to capital](/thoughts/abcs-of-startup-financing) has ever been a truly limiting factor for Australian startups,
but if it was it's less so now.
Foreign investors have less to uncertainty to deal with, and legitimate reasons to be excited. Find a ESVCLP that
aligns with your own investment ideals. Trust their local market knowledge to find the right companies. Know that
[generous tax concessions, government grants](/thoughts/abcs-of-startup-financing), and significantly lower wages than the
Bay Area mean that money will go further. Know that there's a history of using investment to scale cash-flow positive businesses into
global players, rather trying turn around the fortune of companies with huge negative gross margins.
More foreign investment. Greater access to seed capital. Transitioning out of our dependence on property to build wealth. Thousands
of new profitable companies.
Win-win-win-win.
## How to build an ecosystem
I went mushroom foraging on the weekend. They're amazing little organisms fungi.
Delicious and lethal. And your ability to tell the difference requires an ability
to not just observe the small spongy thing sticking out above the ground, but
to understand the broader environment that is surrounding you in the very spot
you're standing.
The spore bearing fruit we see is a tiny fraction of a potentially gigantic living
organism that lives below the ground. Where it decides to grow isn't sheer luck
and accident, because the spores for a particular variety are likely strewn for
kilometers by the wind and have covered almost every piece of dirt and grass
over that distance at some point. And yet here I stood, in one particular place,
where this one particular variety of mushroom has decided to sprout.
It's because I'm surrounded by a mixed wood of eucalypt and pine trees. The spores
lay themselves down and start to produce a root system below the surface that
intermingles with all of the surrounding trees. The thing is the trees need the
mushrooms to survive. The fungi produce minerals and essential elements the trees
need because the soil has become deficient in them. But the mushrooms lack the
green chlorophyll we're familiar with in most other plants, and they're unable to
produce any energy of their own. And so the trees in-turn provide the sugars the
mushroom needs to grow. It's a symbiotic relationship, where all the participants
thrive by supporting each other.
## Filling the tank
When I was younger we had an aquarium. Even now I enjoy the peaceful solitude
that comes with staring at a tank full of beautiful sea life swimming around,
just doing their thing. Anybody who's tried to create their own one at home
knows first hand the effort required.
The constant cleaning. The balancing of
the pH levels. The feeding. The sourcing of fish. You'll add some living
plant life to try help with the oxygenation of the water, but now the
tank seems to get dirty quicker. You get some sea snails to try and keep the
scum on the glass at bay, but it turns out one of your fish find snails quite
delicious.
You add some new fish and discover some expensive lessons
on the circle of life. Not all fish live happily with each other. Some live
at the expense of others. If you want more fish you'll have to get ones
that can't be eaten by the predator in the water. You've ended up with a
shark tank.
## Business & product ecosystems
I had the fortune to be part of a team that built an [amazing ecosystem
of services for app developers](https://elements.heroku.com/). It's success
wasn't just sheer luck either, it was launched with a clear understanding
of what was needed.
The [Wayback Machine](https://web.archive.org/web/20091216065631/http://addons.heroku.com/) doesn't
do the site justice, but if you can read through the broken formatting you'll
see a list of companies and products. None of them adversarial towards each other. All
of them complimentary. And all of them made stronger by each other. Once
you entered the ecosystem as a consumer, it's unlikely you'd use just one thing.
You'd use multiple. You'd become dependent on them. And they in turn on you. Everyone
succeeded together. And we were all better for it.
And yet I'm surrounded by talk of people wanting to build ecosystems. Usually in the
form of geographically defined hubs of economic activity. But the only thing any of the
participants share is geography. There's no common interest. No shared wealth. No
common purpose.
It's just money being throw into the top of the tank to feed a bunch of little fish. And
the only ones getting fat are the sharks.
If you want to build a successful and self-sustaining ecosystem it needs to start
with deliberate and intentional seeding. Of participants that are able to not just co-exist,
but who can develop a symbiotic and profitable relationship with each other.
## The Coders of 2021
> The following is a talk I gave at [The Coders of 2021](http://www.meetup.com/StartupVictoria/events/230191735/), an event hosted by [Startup Victoria](https://startupvictoria.com.au/) event.
When Thomas first asked me to talk on the topic I was wondering what incredible
forecasting abilities I'd need to look that far ahead. And then I realised it's
_only_ 5 years away! While we're excited about the pace of innovation, the reality
is that wide-spread adoption is much slower. And true industry changing innovation
doesn't happen that regularly.

I'd say the last truly major shift we had was with mobile. Smart mobile devices are
now ubiquitous, but looking back 5 years brings us to the release of the iPhone 4S.
How much has that space _really_ changed since then?
My own experience and expertise lies primarily with developer facing tools. The products, services,
languages, frameworks, and technical tools that help turn ideas into reality. So
rather than prognosticate on _what_ people will be working on, I'll stick closer to
my experience and talk about _how_ they'll be working on them. And to predict the
future I'm going to look to the past, further back than the iPhone 4S, back to the
last dot-com bubble.
To plot a trajectory.
And to take you on as rapid a tour of how we're
moving increasingly to the ends of the hyper-generalisation—hyper-specialisation
spectrum.

That's the cost, inflation adjusted, of a single Sun Microsystems Enterprise 4500 server
during the first dot-com bubble. And they were the darlings of that bubble. Any well
funded startup rushed out to fill the racks with them. You'd wait 6 weeks to have it delivered.
If you hadn't already done so, you'd spend that time fitting out a server room. Connecting
the utilities, adding the environmental controls, making
sure it was cooled. Having an E1 line installed from Telsta. Running CAT5 everywhere.
Getting all your Cisco routers setup. Then the
server arrived and you'd spend another week racking and connecting, configuring the BIOS
on it to talk to all your peripherals, compiling the base OS.
It all imploded, the bubble that is, and people (mostly) stopped burning money on servers and data centers. We
bought cheaper hardware that more closely resembled our desktops. But more importantly
we put them into co-located data centers.

That's the cost, per hour, to run a t2.nano server is Sydney. In 2006 Amazon introduced
Amazon Web Services (AWS) to the world. No more building your own data center. No more
provisioning your own fat pipes with a telco. No sourcing your own hardware to rack in
a co-located data center. The hardware was already there. If something failed, someone
was already replacing it.
Others have since entered the market, and as an industry a
huge percentage of us have happily ceded the responsibility of getting a server connected
to the internet to a 3rd party.
This isn't just a story about Moore's Law, and the ever decreasing costs of compute power.
Nor is it a shift from capex to opex. This change has been transformative in both the way
companies go to market and maintain operational agility.
We've given the responsibility of commodotised problems to specialists in the respective
areas. The concentration of effort within a single problem domain, provisioning compute
resources in the case of EC2, provides operational economies of scale to a company like
Amazon that each of us individually would never be able to achieve. It also gives them
a unique position to spot industry-wide patterns and address them efficiently. Again in
a way that we, individually, would never have been able to do.

This isn't just a story of elastic compute power though. Look at the list of problems
AWS alone has solved since 2006. Relational databases. Load balancers. CDNs. Block storage.
Caching. Data warehouses. Full-text search. Streaming data processing. Monitoring.
Log management. API management. Email. Message queueing.
There's an entire supply-chain involved in the delivery of an idea into the hands of
a customer. And each step in that supply chain is becoming increasingly more specialised.
More focussed. And managed by more efficient and more experienced people for a lower cost.
And so the job of a modern developer has become less about knowing how to spin up a
postgres server, because you can have the most experienced team in the world manage it for you
for $9/mo. It's more about being knowledgeable about what the parts are. And most importantly
how to assemble them in the most innovative way.

We've already largely embraced this at an OS, language, and framework level. Rubygems, npm,
pip, godep. Each language has its own package management system to make sharing code easier.
Most developers aren't writing from scratch their own functions to process image uploads
for the site, to resize it into the preferred size variations for icons/avatars/etc.
There's even sites dedicated to helping you work out which particular library you should
use, because there's probably dozens of them.
The next evolution, that is already well underway, is transitioning these standalone libraries
to fully managed services. Because so many of these are iceberg problems, you only ever
see the 10% that's obviously above the surface. But even resizing images isn't really _just_
resizing images. You only need to hit a moderate level of scale and you need to start
thinking about how and where you store them. Do you need to track revisions? You'll probably
need to serve them via CDN. And then you need to reprocess all of them to support some new
dimensions or filter because circular profile pictures aren't the rage any more and we've
all moved to diamonds. Make it someone elses problem.

The vast majority of the problems you face today have been already solved, and productised,
by someone else. What's left is to assemble them in an innovative way. And that's where
we're heading in the next 5 years. Like turning individual Lego blocks into a fully-fledged
Millenium Falcon, developers now need to have an incredibly general knowledge of the blocks
available to them. Most of the job is just knowing how to use them.

At one extreme you have
the hyper-specialised team maintaining a service like Heroku Postgres, their only
job is to ensure the integrity of that database for you. At the other
end of that spectrum you have the consumers of the service. With their hyper generalised
knowledge. Knowing what blocks are available to them. What they do. And how
they fit together.

But the specialisation spectrum becomes fractal. All that ceding of complexity and
responsibility at the hyper-generalisation end buys back time, time to focus on
what's important: what's the one problem you're solving? And so in actual fact it
provides hyper-specialisation within the one problem domain _your_ customers care about.

And so what we end up with is a concentration of focus and specialisation across the industry.
The ability to stand on the shoulders of others. We're not just building apps by plugging
a bunch of battle-hardened and externally maintained packages into our preferred framework.
We're now starting to do the same with runtime and operational concerns. Leveraging
the expertise and experience of dedicated teams to make our own products better.
And the abstractions are only increasing.

Building an Internet of Things platform? Surprise, Surprise! Amazon has you covered. Need
to deploy changes to your fleet of embedded devices around the world? Take a look at Resin.io

Want to somehow add a blockchain network into the mix? Ethereum or IBM can get you started.
Chain.com if you have a need to run it on prem.

Chat bots are all the rage. Don't build your own. You've got Pandorabots, ChatO.ps, Cog.

Why not add some AI or Machine Learning to your IoT blockchain enabled bot platform? Again
AWS have an option for you. As do Microsoft. IBM have APIs to let you use Watson.

Never before in history have so many people been so connected, with so much
access to information. Your job is to know as much as possible, about what is possible. And
leverage it into solving problems in a unique and innovative way.
_You_ are the builders. With so much Lego already at your disposal, the question is
what's the future _you_ want to build? Why haven't you started?
## Adding yourself to the sudoers file
If you've gone at least some way to trying to make your server secure, you wont be running as root so you wont have access to many of the administration commands you'll need. It's easily resolved by adding yourself to the sudoers file, here's a quick guide on how to do it.
Giving a user access to sudo
Unless you've already got an account with sudo access, you're going to need to log in as root one last time to set one up. So do the following:
```bash
su -
```
And enter in the password for root when prompted. Next we'll edit the sudoers file using visudo:
```bash
visudo
```
This will open up the file in a vi session for editing. If you've not got crazy vi chops don't worry, were don't need to do anything complicated. Just page down to the bottom of the document and enter the following:
```bash
your_username_here ALL=(ALL) ALL
```
Hit esc then type:
```vim
:wq
```
and press ENTER. The file will be saved, and you've just granted yourself access to run everything as the root user. Now type:
```vim
exit
```
to log out as root and back in as yourself. Now if we want to edit the sudoers file we can do the following:
```bash
sudo visudo
```
this time when you are prompted for a password, you only need to enter your own one. And tada, the sudoers file is open again. Now let's give some more users access, but we won't be quite so generous with what they can do.
Giving a user access to a single command
What about on a production machine where you've got a user that is a little bit trusted, but shouldn't be given total access to the system? It quite easy to setup some walled access to the commands you need to open up. In the example below, we will grant access to god for the user deploy so that we can start and stop our services through capistrano.
```visudo
deploy ALL= /usr/bin/god
```
Sudo command line howto
How about running a command as another user, without changing to that user? Simple:
```bash
sudo -u otheruser /usr/bin/command
```
Or running a command and pushing it to the background:
```bash
sudo -b /usr/bin/command
```
Or just finding out what commands you are allowed to run:
```bash
sudo -l
```
Sudo config (more detailed sudoers file options)
The sudoers file offers quite a lot of control over exactly what someone can run, as well as who they can run it as, and from where. Let's just quickly run through a few of the other options you've got in the sudoers file:
Restricting who a user can run commands as
The following snippet allows the user bob to run all commands from anywhere, but only as alice or anne:
```visudo
RunAs_Alias HELPDESK = alice, anne
bob ALL=(HELPDESK) ALL
```
Restricting where a user can run commands from
This config allows bob to run any command as any user, but only from the defined subnet:
```visudo
Host_Alias MYNET = 10.1.2.0/255.255.255.0
bob MYNET=(ALL) ALL
```
## Building & Debugging Custom Container Images for Lambda
So I've got a side-project that's one of those somewhat frustrating endeavours. It's just useful
enough that I don't want to shut it down, it's not important enough to justify much of my focus. Which in itself is fine. But inevitably something happens that means it suddenly requires me somewhat urgent focus: a CVE in a dependency that might expose the data I've stored in it, the decommissioning of a service I'm dependent on. It's then that I inevitably discover that the bulk of what I'm running is no
longer officially supported. Heroku has stopped supporting the version of ruby I'm using. The postgres version I'm using is woefully out of date. All manner of local development build chain apparently deprecated.
And so what seemed like it would be a relatively trivial version bump means an upgrading of _all the things_.
## Taking ownership of erosion resistance
One of the propositions of Heroku was that it was "erosion resistent". Which to their credit they've done a commendable job of honouring, I've had joke apps still running there almost a decade later. But they don't make any promises about supporting a particular stack _forever_. Nor do I think they should. But I also don't love the upgrade all of the things at once dynamic that happens when I do need to fix something.
It's been a long time desire to refactor some of these smaller apps to use custom container images on AWS Lambda. For my fairly low-traffic and sporadic loads I don't need permanently provisioned infrastructure. I like the idea of knowing the stack I'm using will be usable for as long as _I'm_ willing to support it. And for my current problem there's a path to make my upgrade hell easier by isolating the offending function into it's own function/container/service and leaving the rest of the app untouched.
## The best laid plans…
A quick skim of some AWS documentation, blog post announcements, and some GitHub READMEs and I'm ready to go. This should be easy. Some minor refactoring a Docker builds later and we get:
```bash
START RequestId: 5a0e91fc-7ae2-480e-af2d-260de6cebf90 Version: $LATEST
time="2021-05-17T07:50:54.186" level=warning msg="Cannot list external agents" error="open /opt/extensions: no such file or directory"
time="2021-05-17T07:50:54.186" level=warning msg="Couldn't find valid bootstrap(s)" bootstrapPathsChecked="[aws_lambda_ric]"
time="2021-05-17T07:50:54.186" level=warning msg="First fatal error stored in appctx: Runtime.InvalidEntrypoint"
```
And here I'm stuck. For hours. Actually, days. I slept on it and came back no wiser to what I'd done wrong. I get it, it needs a `bootstrap` script. But I've got one. In fact in my fumbling frustrations I've got dozens just incase I'd misconfigured or misnamed them. Every permutation and interpretation I could think of is in my container just in case. It's still not working.
It's at this point I should probably abandon this plan and just go make the change on Heroku like I ordinarily would. But... that's not how I do things. It's time to pull this thing apart and understand it properly.
So, dear reader, here's hoping you get value from this bottoms-up dive into how custom Docker images for Lambda work.
## The Steve Austin approach
It's ok, this isn't going to cost $6M. But we do have the technology. We can rebuild this. So it's time to start with the most basic and bare bones container possible, and incrementally step our way toward something closer to the developer ergonomics I'm after. Each way solidifying understanding of how and why things work a certain way before adapting them to my particular needs.
We'll start by using the [barebones shell examples from the AWS docs](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html), which consists of a bootstrap script:
```sh
#!/bin/sh
set -euo pipefail
# Initialization - load function handler
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"
# Processing
while true
do
HEADERS="$(mktemp)"
# Get an event. The HTTP request will block until one is received
EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
# Extract request ID by scraping response headers received above
REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
# Run the handler function from the script
RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")
# Send the response
curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"
done
```
The lambda container will use the script above when it is instantiated to bootstrap itself, and fetch for any work to do. The `$_HANDLER` environment variable is set at runtime and will contain the name of the function that contains our code. The `while true` loop ensures that we keep looping around to process events for as long as the container is alive. The `curl` command will block until we have an event to process so we wont DoS ourselves by looping constantly when there is nothing to do.
```dockerfile
FROM alpine:3.7
RUN apk add curl
ENV LAMBDA_TASK_ROOT=/var/task
ENV LAMBDA_RUNTIME_DIR=/var/runtime
COPY bootstrap ${LAMBDA_RUNTIME_DIR}/bootstrap
RUN chmod 755 ${LAMBDA_RUNTIME_DIR}/bootstrap
COPY function.sh ${LAMBDA_TASK_ROOT}/function.sh
RUN chmod 755 ${LAMBDA_TASK_ROOT}/function.sh
CMD [ "function.handler" ]
```
function.sh
```bash
function handler () {
EVENT_DATA=$1
echo "$EVENT_DATA" 1>&2;
RESPONSE="Echoing request: '$EVENT_DATA'"
echo $RESPONSE
}
```
docker build -t aws-lambda-custom-image-example .
## Local testing
Install the Lambda Runtime Interface Emulator:
```bash
mkdir -p ~/.aws-lambda-rie && \
curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie && \
chmod +x ~/.aws-lambda-rie/aws-lambda-rie
```
Now you can mount the emulator into your container at runtime, and adjust the entrypoint to use it:
```bash
docker run -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \
--entrypoint /aws-lambda/aws-lambda-rie \
aws-lambda-custom-image-example /var/runtime/bootstrap function.handler
```
Test it:
```bash
curl -v -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
```
You'll see a `200` response and the literal string from our function of `Echoing request: '{}'`.
Change the payload (i.e., the `{}` in the `-d '{}` argument).
## Unpacking how and why this all works
Now that we've got a basic working implementation we can use it to understand all of the moving parts. Once there's a solid understanding of all of the fundamentals it'll be easier to rebuild it to do exactly what we need.
### The Lambda Runtime Interface
The [Lambda Runtime Interface](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html) is just
an API contract your container needs to adhere to. The Lambda platform has a certain set of baseline expectations about how it can communicate with you container, and how your container will communicate back. If you don't honour the contract nothing will work.
Our first contact with that is with the URL we provided to the `curl` command. The `localhost:9000` host and port combo is how we target the Docker container we're running, but what about that long path of `/2015-03-31/functions/function/invocations`? Where did that come from? Enter the magic of the Lambda Runtime Interface Emulator we downloaded, and specifically the `aws-lambda-rie` command. We mounted the emulator into the container at runtime as a new volume. We then updated the entrypoint of the container to be the `aws-lambda-rie` which we then passed two additional arguments: the name of our bootstrap script and the name of our function, `/var/runtime/bootstrap` and `function.handler` respectively. Standard naming conventions with Lambda handlers expect the handler to follow the format of `filename_without_extension.method_name`. In our case we're calling the `handler()` method within the `function.sh` file. You'll see in the `bootstrap` script we append the `.sh` to make sure we source in the correct file.
The `aws-lambda-rie` command is what does all the magic of making our function callable via HTTP and at the appropriate path.
It's also helping us manage and process events and state for our invocations. Within the `bootstrap` script you may have noticed we're actually making requests to get an initial event to process, and then sending a POST request with the result when we're done processing. Our container is handling those requests too (i.e., we're querying an API within the same container). That API is all magically available and does what it needs to thank to the Lambda Runtime Interface Emulator.
On AWS these requests would go back out to the platform itself but for the purposes of local testing this is more than sufficient.
## Retrying our custom Ruby container
So here's the `Dockerfile` for my own custom Ruby 2.5 container image which in theor _should_ work based on how I understood the documentation:
```dockerfile
FROM alpine:3.7
ENV RUBY_MAJOR 2.5
ENV RUBY_VERSION 2.5.9
ENV RUBY_DOWNLOAD_SHA256 a87f2fa901408cc77652c1a55ff976695bbe54830ff240e370039eca14b358f0
ENV RUBYGEMS_VERSION 3.1.4
ENV LAMBDA_TASK_ROOT=/var/task
ENV LAMBDA_RUNTIME_DIR=/var/runtime
RUN mkdir -p /usr/local/etc \
&& { \
echo 'install: --no-document'; \
echo 'update: --no-document'; \
} >> /usr/local/etc/gemrc
RUN set -ex \
\
&& apk add --no-cache --virtual .ruby-builddeps \
autoconf \
bison \
bzip2 \
bzip2-dev \
ca-certificates \
coreutils \
dpkg-dev dpkg \
gcc \
gdbm-dev \
glib-dev \
libc-dev \
libffi-dev \
libxml2-dev \
libxslt-dev \
linux-headers \
make \
ncurses-dev \
openssl \
openssl-dev \
procps \
readline-dev \
ruby \
tar \
yaml-dev \
zlib-dev \
xz
RUN wget -O ruby.tar.xz "https://cache.ruby-lang.org/pub/ruby/${RUBY_MAJOR%-rc}/ruby-$RUBY_VERSION.tar.xz" \
&& echo "$RUBY_DOWNLOAD_SHA256 *ruby.tar.xz" | sha256sum -c - \
\
&& mkdir -p /usr/src/ruby \
&& tar -xJf ruby.tar.xz -C /usr/src/ruby --strip-components=1 \
&& rm ruby.tar.xz \
\
&& cd /usr/src/ruby \
\
# hack in "ENABLE_PATH_CHECK" disabling to suppress:
# warning: Insecure world writable dir
&& { \
echo '#define ENABLE_PATH_CHECK 0'; \
echo; \
cat file.c; \
} > file.c.new \
&& mv file.c.new file.c \
\
&& autoconf \
&& gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \
# the configure script does not detect isnan/isinf as macros
&& export ac_cv_func_isnan=yes ac_cv_func_isinf=yes \
&& ./configure \
--build="$gnuArch" \
--disable-install-doc \
--enable-shared \
&& make -j "$(nproc)" \
&& make install \
\
&& runDeps="$( \
scanelf --needed --nobanner --recursive /usr/local \
| awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
| sort -u \
| xargs -r apk info --installed \
| sort -u \
)" \
&& apk add --virtual .ruby-rundeps $runDeps \
bzip2 \
ca-certificates \
libffi-dev \
openssl-dev \
yaml-dev \
procps \
zlib-dev \
&& apk del .ruby-builddeps \
&& cd / \
&& rm -r /usr/src/ruby \
\
&& gem update --system "$RUBYGEMS_VERSION"
ENV BUNDLER_VERSION 2.2.11
RUN gem install bundler --version "$BUNDLER_VERSION"
# install things globally, for great justice
# and don't create ".bundle" in all our apps
ENV GEM_HOME /usr/local/bundle
ENV BUNDLE_PATH="$GEM_HOME" \
BUNDLE_BIN="$GEM_HOME/bin" \
BUNDLE_SILENCE_ROOT_WARNING=1 \
BUNDLE_APP_CONFIG="$GEM_HOME"
ENV PATH $BUNDLE_BIN:$PATH
RUN mkdir -p "$GEM_HOME" "$BUNDLE_BIN" \
&& chmod 777 "$GEM_HOME" "$BUNDLE_BIN"
RUN gem install aws_lambda_ric
WORKDIR ${LAMBDA_TASK_ROOT}
RUN mkdir -p ${LAMBDA_TASK_ROOT}
COPY app.rb ${LAMBDA_TASK_ROOT}
ENTRYPOINT ["/usr/local/bin/aws_lambda_ric"]
CMD ["app.App::Handler.process"]
```
And for the sake of completeness here's the `app.rb` that serves as our function:
```ruby
module App
class Handler
def self.process(event:, context:)
"Hello World!"
end
end
end
```
Towards the end of the `Dockerfile` we install `aws_lambda_ric` which is the [AWS Lambda Ruby Runtime Interface Client](https://github.com/aws/aws-lambda-ruby-runtime-interface-client). Think of it like the emulator, but only implementing the API endpoints required for a production deployment (i.e., not the lambda platform API for getting the next event or posting results back). It also serves as a replacement for our bootstrap script and handles the polling required to hand out additional work.
All the way back at the start of this post it was this script that was causing our error of "Couldn't find valid bootstrap(s)", even though the script exists. Was it not setting the right values somewhere? Is there something else a `bootstrap` needed to do to announce that the container was indeed bootstrapped?
Use what we've learnt from the minimal approach to debug this thing. Time to mount this container just like we did before with the emulator and post an event to it. We'll launch the new container, with the new bootstrap (i.e., `aws_lambda_ric` instead of our script), with the new handler:
```bash
docker run -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \
--entrypoint /aws-lambda/aws-lambda-rie \
lambda-ruby2.5 \
/usr/local/bin/aws_lambda_ric app.App::Handler.process
```
Post an event to it just like before and... 502 bad gateway. Again. Looking at the logs and once again:
```
time="2021-06-07T06:51:55.195" level=error msg="Init failed" InvokeID= error="Couldn't find valid bootstrap(s): [/usr/local/bin/aws_lambda_ric]"
time="2021-06-07T06:51:58.162" level=warning msg="Couldn't find valid bootstrap(s)" bootstrapPathsChecked="[/usr/local/bin/aws_lambda_ric]"
```
Hrm. As we learnt before the `bootstrap` is pretty simple, it just needs to sit there in a loop looking for work. Maybe I've been assuming inferring more than I should have here from the "valid bootstrap" terminology? Maybe this isn't about `aws_lambda_ric` being invalid, maybe it can't find that script at all! Time to connect to that container via an interactive shell and test that theory:
```
$ docker run --entrypoint="" -it lambda-ruby2.5 /bin/sh
$ find / -name "aws_lambda_ric"
/usr/local/bundle/bin/aws_lambda_ric
/usr/local/bundle/gems/aws_lambda_ric-1.0.2/lib/aws_lambda_ric
/usr/local/bundle/gems/aws_lambda_ric-1.0.2/bin/aws_lambda_ric
```
Yep! Definitely a user problem here! It turns out all of the various base images I'd tried had been installing `aws_lambda_ric` into `/usr/local/bundle/bin/` (note the extra `/bundle` sub-directory). Update the `ENTRYPOINT` in my `Dockerfile` (and in the command to test locally) and it works!
And there you have it. Use `aws_lambda_ric` (it's available in multiple languages, not just ruby) and you can turn any custom image into a Lambda-compatible container.
## First, state the problem
Every so often I have a conversation that gives me pause to reflect on how fortunate
I've been to work with the people I did at Heroku, on the problems we faced, and the
opportunities it exposed me to. Yesterday was another of those days. Where a skill that was
constantly coached, refined, and exercised to a point that it feels second nature to me now gets deployed without a thought.
I'm sure anybody who's had the pleasure of working with Mark McGranaghan will be familiar
with the title of this post, "First, state the problem". It's such a simple idea. Before
you rush into trying to solve something, actually define what it is you're trying to
solve. Out loud. In writing. Make it explicit. It seems so obvious it can easily be
misconstrued as a frivolous suggestion.
The reality is that when it matters most, when the pressure is high, and we feel an extra
sense of urgency to find a resolution that's when we're most likely to ignore the obvious
advice. Compound that by having a team of people trying to work together in response to
a problem. Now you've a recipe for either everyone heading off in separate directions
solving a different version of the problem they've internalised, or blindly following the
lead of someone without a clear understanding of what they're actually trying to fix.
## Dumb, predictable machines
Experience in a domain over time introduces subtle little pattern-matching systems, shortcuts
that can often make decision making quicker. Software developers will often talk about
"code smells", a learned sense that says "Hey, usually when I see this pattern it's a sign
that I could be doing this in a better way". The problem with this kind of pattern-matching
is when it breaks, when the shortcut makes you rush into a decision that is ultimately wrong.
It's the difference between what Daniel Kahneman refers to as System 1 and System 2.
System 1 reacts instinctively, System 2 takes the time to re-assess information and think
logically about it.
Mark's wisdom and the application of it at Heroku was less about doing the obvious, but
instead introducing a habit that gave just enough time to question a System 1 response.
To give System 2 time to challenge a possibly incorrect assumption and make sure
you were making the right decision.
I see this all the time with in-the-heat-of-the-moment incident responses. Where there's
a problem, it seems like something is on fire, and "this doesn't make any sense...".
That last statement has become a circuit-breaker to System 1 responses. The moment it
creeps into my thoughts everything stops and I go back to stating the problem without
really realising I'm doing it.
One of the unique benefits of modern software development is computers are boringly
predictable. They do exactly what you tell them to do, every time. If that's not the case
the most likely case isn't that the computer is misbehaving it's that you don't understand
some of the nuance of what it's been asked to do. Step back, state the actual problem. The
problem isn't "I've told it to do X and it's doing Y". The problem is: "It's doing Y, I want it to do X. What have _I_ done wrong?".
Breathe. Take a step back. First, state the problem.
## Getting to the Product Manager interview stage
Eight hundred and fourteen.
That's how many applicant CVs we screened last year for a Product Manager role I had open. 814. Plus the countless number of LinkedIn profiles we proactively screened ourselves, with approximately 50 or so people we reached out to asking them to apply. 64 applicants had a phone screen with our recruiter or the hiring manager (that's me!). 10 made it through to a full interview loop and case study with the broader team. 1 offer went out. 1 offer was accepted.
I wish a submitted CV was a fair and objective representation of somebody's experience and their potential. That isn't the case though. And so we have this imperfect process filled with potential bias made worse by incomplete information. I'd love to be able to speak to each and every applicant to try give them the best opportunity possible to let themselves shine. That's not going to happen with 814 applicants though. There's going to be some sort of filtering required to distill it down to a number we can manage. That might mean we miss out on someone great, and that makes me sad.
So let me try and help fix that!
## Going meta on the PM application
Especially given we were hiring for a Senior Product Manager at the time I was ok not cutting too much slack on CVs that didn't present well. Because like it or not, especially as someone applying for a senior role, your CV is in itself implicity the first test in the process. You have a potential customer (the recruiter/hiring manager) and you're trying to understand their needs and find them a solution (you!) to solve it.
And so that's the journey I'm going to go through today. A look at what it's like on the other side of this review process. The common pitfalls I see. The mistakes people make that either make it hard to get a sense for what they'd be brining to the role or why they're different to the other hundreds of people we're potentially going to talk to.
Because no matter what level of experience we're talking about the best problem for me to have is to have *too many* amazing people with great potential.
## Cover Letters
This has been an eye-opening thing for me and has forced me to rethink some of my own long established process bias. I used to loathe cover letters. Too often they seemed like a pointless procedural thing that people just did because that's what they were told to do. Looking back I think I've also been carrying the scars from hiring many, many years ago. Back when a HR team would physically dump a pile of printed out CVs on your desk to review. And cover letters and CVs would invariably end up becoming separated and you'd get half way through and realise you'd matched up two print outs for completely different people and then... well it was all just a mess.
Technology and process has obviously come a long way since then though.
Correlation is not causation, blah blah. But... I've found it curious often a strong application also had a interest piquing cover letter submitted with it. Addressing the topics called out in the posted job description. Showing some curiosity for the role, at least a passing familiarity with the company or product. It's the opportunity to make the application is tailored specifically to the employer. Which is in part the first step in also trying to communicate that the person applying is perfectly tailored to the role. Back in my old timey days we expected the actual CV to be tailored to the role you were applying for. I've got a half dozen slight variations of my own based on whether I was applying for a management role, one as an individual contributor, early stage vs large scale company, etc..
This is step one in your opportunity to showcase your skills as a product manager. The job description has a list of expectations, you need to show you've noticed them and help connect the dots directly to your relevant experience. Tailor your CV to speak to them directly, or write a cover letter that addresses them. The latter seems far more scalable if you're applying to multiple jobs.
## Information Hierarchy
I worked at a place that specialised in internet marketing and I still remember very trite advice I'd hear in those circles: "The subject line in an email has one job: to get someone to open the email". It's always served me as a good reminder that, especially when you're dealing with an audience that might have limited attention, you've got to give them a reason to keep reading. If you don't want to take your lead from some internet marketing advice (but hey, why not? You are trying to market yourself here!) then maybe you should consider the [inverted pyramid](https://www.nngroup.com/articles/inverted-pyramid/) approach commonly used in journalism, or the [BLUF](https://en.wikipedia.org/wiki/BLUF_(communication)) approach used in military communication.
Hit your notes, and hit them early. Don't bury the lede.
Don't do this:
```markdown
# Glenn Gillen
## Educational Experience
* A college you've never heard of - 20 years ago
## Skills
* Photoshop
* MS Word
* JIRA
* Black belt in taekwondo
```
Instead, consider something like this:
```markdown
# Glenn Gillen
## Experience
### Job Title, Last Company
* Biggest highlight, biggest impact, wow factor moment goes here
```
There's some subtle signaling here in what you think is important and/or your ability to surface what you think the reader wants to learn about you. If you went to some world famous school 20 years ago you think it's worth putting that front and center. Who am I to disagree? But for the rest us, the schooling choices we made a decade or more ago probably aren't _the most important_ thing to tell someone. Same with the skills. Personally it doesn't matter to me if someone knows how to use JIRA or not. It's an interesting tidbit to know but it's also totally irrelevant to whether I'll hire them. Never used it before? It's fine, I'm pretty confident we can help you learn it quickly.
## Highlighting your experience
It sounds simple. Just _highlight_ it. _Your_ experience.
Don't do this:
```markdown
## Product Manager - Company A, most recent
* Engaged stakeholders
* Gathered requirements from customers
* Delivered solutions
* An extensive list of generic product management tasks
## Product Manager - Company B, a little while ago
* Engaged stakeholders
* Gathered requirements from customers
* Delivered solutions
* An extensive list of generic product management tasks
```
Instead, consider something like this:
```markdown
## Senior Product Manager - Company A, most recent
* Facilitated design sprints, including rapid prototyping and user
testing to validate approach for new product launch.
* Scoped down intial launch requirements to ensure a delightful
experience for an initial target cohort.
* Over 40k signups in first month, but with 25% churn. Reduced churn
to 5% within the first 90 days.
* Exited first year as a $100M run rate business.
## Product Manager - Company B, a little while ago
* Worked with company leadership to craft 1, 3, and 5 year product
vision.
* Mapped vision to a list of concrete initiatives.
* Used a combination of customer interviews, surveys, and A/B testing
of early proof of concepts to validate problems. High collaboration
with engineering peers through this process to iterate towards potential
solutions.
```
I'm not going to say the first example is bad per se. Because it's the format of about half of the applications that came in. Therein lies the biggest problem though: you're completely undifferentiated amongst a sea of hundreds of other people. The other problem with the first approach is it doesn't really tell me much about what you've actually done. It's a collection of overly abstract process bullet points. There's no evidence of how or where they were applied. No outcomes. No sense of what exactly you've worked on. What's interesting about _your_ experience? No growth or development. Just generic PM experience after generic experience, each one the same as the previous, and the same as everybody else.
## Reflection & Iteration
At the end of the day though it shouldn't be about copy & pasting someone elses playbook or blindly
embracing their opinions. It's about developing a process, being reflective of what's working and what's not, and iterating on it. If you're not making it to the interview stage you need to work out why. Try arranging for 5 of your friends to get on a video call, give them 3 minutes to read/skim your CV or application, and then ask them what they can recall from it (without referring back to it). Is that the same few points you want your next employer to notice? If not, how do you bring more focus to the key points?
The main job of your CV in a job application is to get you the next stage. Give the hiring manage a reason to _want_ to call you.
## Investment Flashback - Multitudes
Next on my February Funding tour: [Multitudes](https://www.multitudes.co).
I've a friend who runs a VC firm that does a mix of investments directly out of a fund alongside some syndicated deals. Knowing that I have a particular skew towards developer tooling she reached out to me to do some light due dilligence on an investment they were thinking of making and to get my read on the product.
## An unflappable confidence
My first intro to Multitudes was less an intro and more of a "here's a copy of the video from the investor presentation they gave to us last week". You know when you watch someone talk about a topic, and you can tell they _know_ their topic? Not because they're overwhelming you with information but rather because they themselves seem unable to be overwhelmed. No question ruffles them, they seem genuinely excited to be able to talk about it, and can keep seamlessly bringing the questions back to to overarching theme.
This was one of those calls.
Lauren just seemed to genuinely love this problem space. It made talking about it seem both easy and exciting. And lets be clear, Multitudes is about helping engineering managers and teams be more productive. It's not on its surface the most exciting of topics.
## A problem that resonated
I've oscillated between being an individual contributor and a manager multiple times over my career so I can sympathsize with both sides of the table when it comes to anything in the realm of "performance management". None of this is an issue for anybody when everything is going perfectly. But things are rarely going perfectly, there's always some room for improvement, and so identifying where that is and what enables it is where the tension arises.
As an IC it's that discomfort of working out how to provide constructive feedback to someone else, especially your manager, without the risk that it's received poorly and impacts your working relationship. Often people just don't think it's worth the risk and try to swallow the frustrations and/or eventually leave. Then there's the parts of this area that less about you explicitly taking an action but rather how others are objectively measuring you. Key Performance Indicators, Lines of Code changed, ping pong points won (I assume, it's been a while since I've lived in SF 😉). Now you feel like some overlord is trying to squeeze every last ounce of effort out of you to hit some arbitrary number. It feels gross. It takes the fun out of what you're doing.
As a manager it's not as much fun as ICs often think, especially for those that have been promoted from an IC role into a managerial position within the same company. It becomes pretty lonely very quickly. People you were friends with before are still friendly and social, but it's a bit different now. They're understandably at least 10% more guarded about what they say to you about work because you're part of the machine. You have to do the same because there's things you legally can't share with them, and because you can't be playing favorites with specific employees. There's an uncomfortable balance of staying on message and keeping the official company line which means you can't be your friend and team mates personal work therapist anymore. Meanwhile you've been thrust into a job you've never done before and so you're almost certainly terrible at it. Now you have a dashboard full of metrics reminding you every single day how terrible you are and how unhappy you. You know! You feel it every day.
## Non-creepy metrics
Through all of that, it's hard to see how metrics actually help. ICs feel like big brother is scrutinizing their every move, managers feel like they're being reminded every day of how they're failing their team (and in turn being judged by _their_ big brother).
Part of the pitch Lauren gave included a story about an engineer who'd had a bit of a slump. At least others perceived that to be the case. Someone who was previously seen as a high performer and now suddenly wasn't. As a manager, how do you resolve that? Speak to them about it is the obvious and Management 101 answer. It's not always that easy though. I've been on both sides of that conversation and when you're in a slump sometimes you don't even know why. Everything suddenly seems harder than it used to be. You're constantly being interrupted or distracted, and your efforts to fix it don't achieve much. You start the week full of energy and it just seems to dissipate. So when your manager asks "What's wrong? How can I help?" you just... don't know! Now what? Set some specific outcomes and objectives? You've already got work to do, you don't need a reminder of that. You're not an idiot. But the re-formalisation of this suddenly feels an awful lot like you're being put on a performance plan. "Am I going to lose my job? Ugh. This stress probably isn't the help I need to get out of the slump!"
Anyways... back to Lauren's version of this story.
The person in the story had been a high performer. One of the more junior ones, but they'd quickly grown to be a much respected leader in the team. So much so that the other leaders in the team stopped helping, "You're doing great! You got this!", except they didn't got this. All that success had been built on the back of exciting collaboration. Now this high performer had found their own version of being in a lonely place. They didn't have the support network they used to. Those people were still there, and still willing and able to help, they just weren't. Their attention had moved elsewhere. The dynamic had changed, and so did the results, and eventually the morale of the person too.
_(Aside: One of the findings in the research conducted for "12: The Elements of Great Managing" was that team members are more frustrated by someone who is capable but lazy than they are someone who is incompetent. That is, if you know someone is able to do the work but doesn't seem to be pulling their weight it will have a larger negative impact on your own mental state than someone who is straight up bad at their job. So the impact of someone who's in a slump like this can cause very real collateral damage to the team as a whole and not just the individual)_
How did they work this out? By seeing the communication graph between people. I'd long known this kind of detail was important, I've always tried to make sure people on my team have regular skip-level chats with with any people above me on the org chart. Who you talk to, and who remembers your name, can have an outsized impact on your career development. It shouldn't have surprised me as much as it did to see it at a more team-local level too. And once I did it slapped me in the face so hard, I could see it everywhere now. Juniors who plateaued because their support networks rolled on to supporting the next junior person. Staff/Principal engineers who were frustrated because they now felt like they were on the critical path for providing input on every single thing everyone was wanting to do and the context switching was breaking them. So many of the problems I was witnessing around me seemed like they could be explained by a diagram or two. People with too few lines connecting them to people who could inspire their growth, people with too many lines having to carry too much load, lines that were too fat showing too much volume on specific things.
## Improving human outcomes
What really landed for me with Lauren's pitch just how empathetic the whole thing was. This wasn't some "poor productivity is costing the industry seventy squillion dollars a year, we need to work people harder" story. It wasn't a "you need to connect individuals and their commits to the the impact it has on the bottom line" story. It wasn't a "here's how you stack rank your best performers and fire the rest" story. It was a story starting from an assumption that the people you hire really are trying to do their best, but things get in the way. Things you can't always see or notice. But if you could... then you could remove the impediments. It's a story that believes in the best of people and that we just need a little help from time to time, which is one I already believed.
And so instead of just doing the due diligence I asked if there was any room to squeeze into the syndicate. And here we are.
Obviously if you've read this far you definitely need to sign up for [Multitudes](https://www.multitudes.co/join-our-beta) and start helping your team (or introduce it to every engineering manager you know).
## Investment Flashback - Tractor Ventures
Third in my series of February Funding Flashbacks is [Tractor Ventures](https://www.tractorventures.com), and this one really does have quite a flashback. It's probably the most personal of the stories to tell, and one that spans many years rather than just a couple of conversations. So strap yourselves in...
## Leaving the Bay Area
When my time at Heroku came to an end my family and I moved back to Australia. We didn't really know what we were moving back to though. I'd originally left with my girlfriend for a long overdue holiday in Europe, in large part because I was tired of my job at the time but couldn't find a better one (because my job at the time was actually pretty great!). Several years, an engagement, a marriage, multiple cities we'd called home, and a newborn later and we were coming back to... what? Family and friends, obviously. A beautiful city to live in and raise a child. But for work? My last memory of it was a country with relatively few tech jobs. Living in San Francisco you're certainly not being overwhelmed by the volume of startups out of Melbourne and Sydney that are "changing the world".
So I came back, hacked on a few things that went nowhere, and went to a bunch of meetups. It turned out there was heaps of interesting tech activity happening if you went looking for it. In the midsts of this aimless wandering I'd regularly end up chatting with Matt Allen. He ran a recruitment agency (with its founder, Steve), one of the few I've met that seemed to actually do a good job, and somewhat uniquely biased towards being useful to candidates. Recruitment and recruiters operate in a weird dynamic. As a recruiter you're hired by an employer to help them find candidates, you go through your network to find them the perfect match, you convince that person to leave their job for a new opportunity, and now you get to backfill the very position you just made vacant and the cycle continues. It's so rife with perverse incentives and conflict. Which is, at least in part, why I think so many tech workers treat recruiters with high levels of skepticism. You can feel when you're treated as a number being thrown into a high volume process and that they only care about you for their commission. Anyways, this is all a long digression to be able to say: not Matt. Candidates knew he had their backs, and so he was a welcome feature at every tech meetup (equally welcome was his sponsorship of most of them, I'm sure).
It turned out that not only did smart tech-minded people turn to Matt when they wanted a new job, they turned to him when they had a side project that needed investment to _turn into_ their new job. So we'd talk about that. A lot.
## Trying to be useful
In the midsts of this wandering I'd managed to find myself semi-regularly hanging out with other investors, founders, would-be founders, and having lots of interesting conversations. I didn't have much on, so I setup a booking link to make myself availble for a couple of days a week for anybody who wanted to grab coffee and chat about what they're working on. I pretty found myself fully booked and over-caffeinated most weeks.
Some pretty quick patterns emerged from those conversations. There were more people working on more interesting things than I'd realised. Some of them seemingly unaware of how well they'd done with basically no funding (there wasn't much of a VC industry in Australia). People who'd done quite well building a business selling to other local teams, but didn't really know how to grow a go to market function. And, sadly, far too many people getting absolutely fleeced by bad advice/design/consultancy from agencies who'd filled the gap in the market for people who'd been attracted by the idea of "let's build a startup!" and then didn't actually know what to do next.
## Hatching a plan
Over the dozens of conversations I'd had with Matt, an idea had started to emerge. In hindsight it was a terrible combination of things trying to be mashed together. Part solution to actual problems, part social/capitalism/financial experiment, and probably a bit too much of that Silicon Valley "gotta change the world" / disrupt-all-the-things I'd brought home with me.
Something else I'd brought home with me was a soured taste of the Venture Capital industry. I'd seen people I knew build great businesses, the kinds of businesses that employed dozens if not hundreds of people that generated revenue most people would be envious of, contort themselves and their companies in the unhealthiest of ways chasing all-or-nothing outcomes. Raising their Series D rounds and knowing that a $1B exit would barely be enough to satisfy people now. WHAT?! Pouring almost every waking hour of their lives into their companies for a decade and it just never seeming like it would ever be good enough, even with everything seemingly going so well. Perception is relative, but so many people seemed to have a warped worldview. That's not to say there isn't a place for VC, but the growing allure around startups and the associated mythology had founders believing it was the one true way. Matt & I thought there had to be a better way. We weren't alone as Bryce was trying to start Indie.vc in the US around the same time and so we compared some notes and learnt a lot from his early experiments and structure.
Another thing I saw in SF was [Heavybit](https://heavybit.com). One of the Heroku founders built something that seemed quite special. A dedicated space for emerging devtools companies to work from. My team at Heroku had the privilege of working there for a while so I got to see the magic up close. There was just something special about the forced serendipity of having ambitious people sharing a space together, having lunch together, building products together, and probably most importantly selling them together. The companies in the space weren't adversarial, many were actually incredibly complimentary. So you'd see one company successfully close a sale to Customer A, and then the founders of other companies compare notes on what's working and what's not. A rising tide truly lifting all ships. I wanted to replicate it in Australia too, so for a while there I was talking with Heavybit about whether we partner up or whether Matt & I just blatantly rip-off their idea (with their permission).
What I didn't like about the Heavybit model was terms for the startups (at the time), basically you got free rent and support (there was a regular rotation of literal world leading experts in the building each week) for an equity stake in your company. That might have made sense in the Bay Area where these companies have raised VC, but that wasn't a realistic starting assumption for an Australian startup. And besides, we didn't want to force them to take VC. Instead we should invest in the companies.
We needed to raise some funds to make that happen. I was particularly big on aligning incentives everywhere. The idea of running "a fund" seemed misaligned with what I was wanting to achieve. We're telling these startups there's a better way to fund your companies, without perversive incentives that will drive specific outcomes in specific timeframes and then what... we'll go raise a fund that has a fixed term on it? No. Not that. We'll create a company. Our investors can invest in our company, the company will in-turn invest in the startups. We'll structure the deal a lot like Indie.vc did so it maximises optionality for the founders: build a sustainable business and we'll take a percentage of revenue up to some maximum pre-agreed cap, if instead you decide to ride the rocket ship and take VC then we'll convert into a traditional equity agreement and join you on the ride. We want founders to be aligned with helping each other too and really intentionally driving that rising tide raises all ships aspect, I guess that means founders should also end up with some equity in _our_ company too? Sure, why not!? We're here to change the world and show everyone there's a better way.
We could also standardise all of the other good-for-the-world stuff at the earliest stages with founders and really pay it forward. Should we all be B-Corps? Let's get that whole [Pledge 1%](https://pledge1percent.org) thing into all these companies. The list went on and on and on.
The outcome would have been a Berkshire Hathaway conglomerate that owned chunks of a series of complimentatry businesses that were throwing cash back to be re-invested into doing more of the same.
## The harsh truths
There's still a tiny part of me that wonders... what if? What if we'd managed to get this co-working incubator not-a-fund congolmerate off the ground. Then there's a large part of me that's super thankful we didn't get stuck holding commercial office space coming into 2020.
The reality from our would-be investors was basically:
- "Let me get this right, you need _a lot_ of money to... have a physical space, where you can fly world leading experts in, to help make a bunch of startups in that space more successful. But the startups in that space won't be paying rent, instead you'll be _giving them_ money. And maybe, but not necessarily, getting equity in their startup in return. Either way you'll _also_ be giving them equity in _your_ company. The hope is eventually some subset of those startups start generating revenue and can pay you some of your money back. All of this money you're giving out is coming off the company balance sheet rather than a fund, and so not only is there no timeline to when I as an investor could expect a return but there's no actual specific mechanism to make that happen. And that if you somehow miraculously pull all of this off, the revenue streams compound to fund your growth projections so there's no expectation of a later funding round where I could exit... ?"
- Me: "Correct."
Look, I didn't say it was a _great_ idea. It would have been a heck of a lot of fun. I think we could have helped a lot of people. The odds of it working out for our investors was slim though, and how the world has changed since then only made the odds tougher. That said, a handful of people were willing to take a risk on us pulling it off which I find amazing and incredible and all kinds of humbling given what we were asking them to believe. Unfortunately it wasn't going to be anywhere enough and so we never took their money.
## A different way to help startups
Somehow in that process Matt and I met Ian Gardiner, who was heading up the Startup team at AWS in Australia and New Zealand. As the reality was sinking in, Matt and Ian had been talking about Matt joining the Startup team. Matt was a perfect for that role for all the same reasons he was great for our venture: he loved talking to founders, and they wanted to talk to him. He seemingly knew all of them already from being a recruiter. It was a great match. So he said farewell and joined AWS.
I've joked with him regularly that "once a recruiter, always a recruiter" because I think it must've been literally his first day when he referred me in as a potential candidate, and convinced Ian they should find a role for me. Ian is a pretty good judge of character and he must've been able to read me like a book. "Look man, you don't strike me as the kind guy who wants to run a finance company. And that's what you'll end up doing if you pursue this. VC, or anything like it, is a finance job. You seem like you just want to hang out with smart founders and be helpful. Come do it with me and instead of doing it for free you can get paid, and we'll cover the coffees you're shouting everyone too". He pretty quickly got me over my hesitations about working at Amazon, and possibly for the first time ever I was studying for a job interview like I was studying for an exam.
I got the job. Matt and I spent the next two years sharing a desk and helping startups. I skewed more towards the product and tech side of things, Matt towards people. By people, I mean everything that happened outside the cloud. Want to talk to people about your startup at an upcoming event? Let Matt get you on a panel. Thinking about raising and wondering which investors might be interested? Matt. I'm struggling with sales and want to speak to other founders who have had or are having this problem. Clear out Monday morning, Matt has booked a breakfast for all of you to get together. It was a fun double act while we got to do it together. It became pretty clear that "once a recruiter, always a recruiter" was missing an adjective: _great_ recruiter. Even that is too reductive, because to be a great recruiter is really about listening to people. Lots of people. Hearing one person say they have a problem and then remembering that some other person had the solution. Your superpower is being in the information flow to able to pattern match and then connect people. Matt is great at it. And with the scale that AWS makes you operate people noticed. Lots of people. Matt could now make some magic happen...
## A different different way to help startups
So when I left AWS I decided I'd get back into being operational within a startup and prove to myself I could actually still do the work. Matt however had some unfinished business. Even though in the intervening years the Australian VC industry had grown significantly there was still a belief there was something missing. A way for ambitious founders to get the money they needed to grow without giving up a huge chunk of their company. Something that allowed them to maintain optionality. The kind of founders who had built strong, dependable businesses that just needed a bit more fuel. Tractors, not rocketships.
[Tractor Ventures](https://www.tractorventures.com) was born. A rational re-interpretation of our earlier "something other than VC", stripped of all of the ridiculous additions I was trying to throw in there, packaged up in a way that actually made sense to both investors and founders. So when Matt came knocking he didn't really need to pitch me much, it's not like I could say I didn't believe in the idea! We both wanted to see this happen. The only decisions to make was 1) how much? and 2) which vehicle/entity? Because like before there's still big dreams about the company itself, and still the same belief in aligning incentives. So there was the opportunity to invest in Tractor itself, or into the debt facility that would be loaned out to the startups.
I decided the debt approach was the novel and not-VC bit and so that'd be what I'd do. Until my wife interjected, "What? It's Matt. You've been talking about this for years. Step up. If he's offered you both you invest in both".
And that, kids, is why I married your mother 😁
It's been such a joy watching Tractor go from strength to strength, to see that a kernel of that original thesis was obviously correct, and for Matt and the whole team to have done such a great job at stripping back all the bullshit to find that success. If you're a founder in Australia or New Zealand and you're at the stage where you need an injection of capital to fuel your growth you should definitely check them out.
## Kaizen via AI - Part II
This is part of an on-going series about how I'm intentionally improving my… productivity? … efficiency? … creativity? … happiness? 🤷♂️ Working that out is part of the journey too. [Here's part 1](/thoughts/kaizen-via-ai-part-1).
## ChatGPT: My New Daily Driver
I've found myself using ChatGPT a lot more than Claude lately. Not because I consciously decided to switch, but because it kept showing up as useful in ways that were hard to ignore. Image generation was the initial pull. I tried it once, got intrigued, then kept coming back. It's not the only tool I use, but it's become a daily companion for more than one type of task.
### As a Scratchpad
This blog post? It started as a two-minute ramble into ChatGPT while I was sitting in the car. I've been treating it like a rolling notebook that actually understands structure. It helps me capture ideas, polish messy thoughts, and convert short bursts of input into something coherent. It's surprisingly good at cleaning up the rough edges — ums, backtracks, and half-finished ideas all come out the other end more refined.
### As a Scriptwriter
I needed to create a product advertisement recently, and we joked internally about doing it in a TV infomercial style. I asked ChatGPT to draft a script, and it absolutely nailed the vibe. It helped unblock the creative process, gave me a launchpad, and made the whole thing more fun to think about.
### As an Unexpected Animation Assistant
For a recent product demo, I needed to create some custom animations. I assumed I'd need to dive into Blender or Canva — tools I'm not especially familiar with. I asked ChatGPT for options, and it suggested Manim, a Python animation library I hadn't considered. Turns out, writing Python is way faster for me than fumbling through a GUI. I was able to build something tailored, reproducible, and exactly what I needed — no new toolchain required.
---
## Cursor: Great for Prototypes, Not for the Finish Line
I spend a good chunk of my day in Cursor, using AI to help scaffold ideas and code up demos. For disposable work, it's fantastic. It helps me move quickly, iterate fast, and test wild ideas without much investment.
But the experience doesn't scale linearly. Once the surface area of the code grows or the interdependencies start to stack up, I end up fighting it more than I benefit from it. There's a steep drop-off in usefulness once the work stops being disposable.
---
## Vercel V0: Inspiration-on-Demand
One evening, while walking to dinner, I had an idea for a landing page we'd been meaning to build. Instead of jotting it down, I opened up V0, typed in my idea, and closed the app. After dinner, I reopened it to find that it had built a functional draft layout from my prompt. That's the kind of acceleration that turns random inspiration into progress, with zero disruption to what I was actually doing.
---
## Gemini: Solid Market Research Acceleration
We've been testing Gemini's deep research capabilities while working on a new product. We asked it to act like a world-class marketer and write a market research report and execution plan. I gave it minimal context at first, just to see what it came up with, and then refined it with more details over time.
It didn't blow our minds, but it absolutely saved time. Especially for outlining content plans, summarizing positioning, and organizing the landscape. Nothing strategic came out of it that we hadn't already discovered through our own user research, but for compressing hours into minutes? Not bad at all.
---
## Blender (via MCP): Building with My Kids in Roblox
My kids are into Roblox, and we decided to try making some custom assets together. We started with an avatar and used the Blender MCP server to generate the base model from a simple prompt. None of us had used Blender before, and to be honest I hadn't spent much time in Roblox Studio either, but it worked.
Now we're at the "finishing touches" stage, which means I'm learning Blender properly to tweak things. It's a familiar pattern: AI gets you 90% of the way there, but the last 10% still needs human finesse. That said, it was a great intro for the kids, and it gave them a taste of what's possible. They don't fully appreciate how magical that is yet. But I do.
---
## MCP Servers: My Personal Workflow OS
I've been quietly building out a suite of small MCP servers to automate and organize my day.
### Meeting Prep with LinkedIn Context
One of them helps me get ready for meetings by pulling in calendar events, filtering out internal attendees, and then using a browser automation layer to visit external participants' LinkedIn profiles and summarize their backgrounds. It saves me from scrambling five minutes before a call and gives me more context than I'd have time to dig up manually.
### Tab Management at Scale
Another one helps me manage the 355 browser tabs I currently have open (yes, really). Every one of them represents something I'm *definitely* going to get to eventually. To avoid drowning in them, I built a browser MCP server that lets me search, organize, and re-surface specific pages.
Even better, I can just tell Claude something like:
**"Open the browser tab that contains the Google sales document."**
And it'll go find it and bring it to the front.
It's a small thing, but that kind of tool feels like a productivity superpower. Especially when you're balancing deep work with fragmented attention.
---
## Voice and Motion: Multitasking Without Losing the Thread
One of the subtler benefits of all these tools is how they let me *move* while still making progress. I can capture ideas as they come to me. Whether that's narrating thoughts into ChatGPT, firing off a V0 prompt between errands, or quickly slotting a distraction into a tool that does something with it while I focus elsewhere.
Instead of losing momentum or letting inspiration evaporate, I can drop ideas into a system that keeps working while I shift gears. Sometimes it writes something. Sometimes it builds something. Sometimes it just holds the thought until I can return to it.
It's not just a better productivity model — it's a more humane one. Fewer tabs in my brain. More room for the real work.
---
## Final Thoughts
All of this fits into the same theme I wrote about previously: continuous improvement, low-friction experimentation, and getting comfortable with tools that don't need to be perfect to be powerful. It's not about outsourcing everything. It's about keeping momentum, reducing friction, and buying back time.
And maybe the most surprising part? This feels more human, not less. I'm spending less time on setup and overhead, and more time thinking, building, and talking to people. That's a trade I'll keep making.
## Kaizen via AI - Part I
This is part of an on-going series about how I'm intentionally improving my… productivity? … efficiency? … creativity? … happiness? 🤷♂️ Working that out is part of the journey too. [Here's part 2](/thoughts/kaizen-via-ai-part-2).
A couple of weeks ago I caught up with [Mark Ghiasy](https://www.linkedin.com/in/markghiasy/)
for lunch and as is wont to happen when a couple of nerds catch up for lunch
these days the conversation rolled around to AI.
> * **Mark:** "So, are you using it much?"
> * **Me:** "Yeah, I dabble. Bits & pieces here and there"
> * **Mark:** "How are you using it to improve your processes or productivity?"
> * **Me:** "I mean... I... It's often that I remember too late. Like 'ah! I could have
> delegated or augmented that task with an AI tool!"
> * **Mark:** "So when have you scheduled yourself to change that behaviour?"
> * **Me:** "uuhhhh..."
> * **Mark:** "Come on man, you know how this works. If you _actually_ want to change
> the behaviour you need to be intentional and committed to making it happen.
> _When_ are you doing this? Literally, what time every day?
> * **Me:** "..."
> * **Mark:** 🤨
> * **Me:** "Ok ok! I'm putting it in my calendar right now to review everything I
could have or should have done differently and making a plan for the next day"
And so for the past couple of weeks I've been making a serious effort to review
my daily activities and look for areas where I could have improved the way I
did things by some way incorporating one or more AI tools. And "improved" is
maybe setting too high a bar there? Just forcing myself to have more exposure
_every single day_ to ensure I'm keeping across of developments and having a
clear understanding for areas where it's useful and areas where that's not
realistic. The reality has been that outside of whenever I'm on a call or meeting I'd
estimate a out 90% of my time has been in some heavily AI-driven or augmented
workflow. Even when I'm on a call the vast majority of those are recorded,
transcribed, and summarised by an AI tool.
My plan is to publicly document my journey here. Partly to keep myself honest
and accountable, but also to share the process so others can either replicate
it or just take a shortcut by copying the things that are working for me.
So without further ado, part 1 in this series of how I'm making constant
incremental improvements through the use of AI.
## Desktop, Mobile, and Local LLMs
I'm a very happy (paying) user of [Kagi](https://kagi.com) for search and had
already largely dropped Google. I have also found myself leaning more on ChatGPT
for answers to certain questions like "How do I do X in Screenflow on Mac?". I
don't always know the correct name for the feature I'm looking for which can
make finding the answer in the official docs difficult. There's often a dozen or
more videos explaining how to do what I want but that can mean scrubbing 15
minutes of video just to try and find the name or location of the one menu item
I'm looking for because I don't want a whole tutorial I just can't find the
thing I'm after! These combined search + summary for a specific and well defined
context have been great. I've also observed other people lean heavily on
ChatGPT and Claude as their primary interface for answering questions (i.e.,
not using a search engine at all). It's definitely felt like I'm witnessing a
generational divide happen in real time as the kids these days shun their
grandparents' search engine approach to finding answers.
To try and help me push past this I decided I would make accessing LLMs easier
and more prominent on my systems. So I installed:
* [ChatGPT Desktop & Mobile](https://openai.com/chatgpt/download/)
* [Claude Desktop & Mobile](https://claude.ai/download)
* [Ollama](https://ollama.com) (and the `gemma3:4b` model)
The latter because I felt like being able to running a model locally would
become a useful thing. Especially in situations where I might have data (e.g.,
my own personal or financial data) that I'm not comfortable sharing with a
hosted and shared service. So far I've not done more than play with it but I
think those use cases may still present in the future.
My hope with the mobile apps is that if I'm out walking and inspiration strikes
I can use it dictate my barely-filtered thoughts and summarise or apply some
structure to them later. Another scenario that is aspirational at this point
and not something that's actually been added into my routine.
What has become wedged into my routine is using the desktop apps! Looking at
my chat history I'm clearly giving preference to Claude here, though I don't
have a rationale or data driven explanation for why that's the case. The
answers from both, for the types of questions I'm asking, seem fine and not as
though one is objectively better than the other. What has been interesting to
observe is I'm not using either of them at all like a replacement for search.
The chat history shows a list of seemingly random and scattered ideas across
a vast range areas. They're functioning almost like a dumping ground for me
to put whatever distraction pops into my head that I'd love to come back to
but probably don't need to spend cycles thinking about right now. Like an
idea I had for a hardware project that would involve a raspberry pi, an NFC
reader, and some custom 3D printing. I was able to ask "What are the ways
I could design and build a system that (detail the requirements/features/etc).
Include in the options how and where the this process could be outsourced in
addition to how I would build it myself". It's immediately out of my head and
while Claude was generating a page of options for me I could get back to work.
Later that night I came back to read what it had produced and it seemed like
pretty good outline and actually suggested a feasible way for me to get what
I want!
Another use case that's becoming increasingly common has been to ask for help
on using these AI tools. Sometimes that's because a blank canvas is overwhelming
and it's not obvious how or where to start (Claude again provided some great
tips when I asked how I should use the mobile app to be most productive). The
recurring example is when I'm just feeling mentally fried and need a little
help with my own thinking. "What questions should I ask about X?", "I'm writing
a blog post about Y and need an image for the top of the page. What are some
prompts I can use to generate an interesting image?". There's been something
valuable about continuity across days with this approach too. It's no surprise
that this brain fade is most likely to happen at the end of a long day. A tip
I was given a long time ago when writing is to leave your last sentence of the
day unfinished, as you'll find it much easier the next day to pick back up
where you left off. In a similar way, going back to my chat history first
thing in the morning and acting on the last result has made it feel easy to
get straight back into things each day.
## Vibe Coding
I used Co-pilot in the beta and it felt like magic. I keep hearing about how
amazing Cursor and Windsurf are. Even though I'm not writing code day-to-day
I figured I'd owed it to myself try the latest tools and see how far things
had come. Maybe it's just a frequency illusion because now I'm paying attention,
but it also seems like "Vibe Coding" has hit almost meme levels of commentary
over the past 3 weeks so my timing here definitely feels exciting and like
fun things are happening in the general vicinity.
**🤩 And wow, this stuff is amazing! 🤯**
I was seriously blown away by how good the output was from my first attempt
at using the AI coding agent in [Cursor](https://www.cursor.com/en). I am not
a Swift developer, I have never at any point in my career have I been. I gave
Cursor a brief on a desktop app I wanted to help automate some person tasks,
and ended the brief with "Ask me any additional questions you need answers
to before implementing anything". It asked for a whole bunch of extra
information that was obviously valuable to know when designing a desktop app but
not something I'd considered given it's not my area of expertise. I answered
those questions and within a minute I had a fully working app! Sure it had
a couple of rough edges, and we iterated and fixed some of them simply by saying
things like "Change the icon of X to be something more visually appropriate for
the content". For an app for personal use, and something that took literally
_minutes_ to create, it was incredible.
Next up was a website to serve as an asset for some product discovery and
validation. This was another mind blowing experience. I had specific opinions
on what I wanted here so I pointed Cursor at the framework and component
libraries I wanted to use, gave it the brief, instructed it to ask me questions,
I answered them, and again within minutes I had a result that I think may have
taken me a day or more to roll myself if I'd gone that path. It wasn't perfect,
but even some of the imperfections were quickly addressed through prompting. At
one point I gave it an instruction like "The way the sidebar navigation is
collapsed and expanded looks gross. Come up with a better UX that
maintains the functionality I requested" and it did! A really nice UX! One that
is better than I would have done myself (I might have opinions on what I like,
but I'm not great at the design parts!).
The whole experience made me think this is such a huge game changer for Product
Managers & Designers. No more click-through wireframes. No more having find
non-existent slack in a schedule to get an engineer or two to help spike out
a really speculative prototype. It's now totally feasible to get real people
to use something that _looks and feels real_. The quality of the feedback and
learning we can get from that is incredible. The conviction we can build
_before_ committing an engineering team to building a new feature will be
amazing. There's just sssoooo much iteration and learning that can happen
on really quick cycles with prototypes we can happily throw away because
they took us minutes to build.
The other use I've had over the past week or so was getting my blog back to
a publishable state. I turns out it's been a couple of years since my last post
(where did that time go?!) and a series of dependency issues meant I couldn't
actually build my site any more. Upgrading to the latest version of Gatsby was
proving to be difficult given some of those dependencies no longer worked, and
overall it felt like everything about my tooling and pipeline was overkill for
what should be a static site. I tasked Cursor with taking the markdown content
from my current site, and making it work with Astro instead of Gatsby. And...
it did?! You're reading the result right now! That was almost pretty magical.
It created a new working directory, pulled in the dependencies, wrote a
migration script for my markdown and MDX files, got me to run it and then
start the server, automatically detected the error the server had parsing some
of the migrated content, re-wrote the script and had me re-run it, repeated that
loop a couple more times until the server started successfully, and here we
are! I came in and updated some of the styling and component choices and pushed
my changes and here we are. Again probably just a few minutes of waiting
and pushing the occassional "run" button and it did everything else
automatically.
It's not all unicorns and rainbows though!
The deeper I got into my projects the more that first use experience seemed
to fade into a distant memory. In typical AI fashion I'd find Cursor (or
more specifically I guess, the Claude Sonnet 3.7 LLM it was using) would be
confidentally wrong about things if and when things didn't work as expected. It
seemed to get confused about which version of the docs it should use for the
frameworks that were being used (Svelte and Tailwind CSS in this case) and would
regularly implement features based on the way earlier versions would work.
When presented with the error it would then often take drastic actions like
pinning everything to earlier versions and then reimplement basically the entire
site (and it still wouldn't work!). I've quickly developed a muscle of reverting
any fixes if they don't work the first time and just going through the debug
and resolution process manually.
Similar frustrations emerged when I wanted to replicate existing functionality
but in a new way. "I want to allow users to create a new Company. Add a new
company page that captures the required details, base the design and form input
on the existing New Person page". A minute later and I have the New Company
page... I also have a whole new library for managing form inputs, a page
that looks nothing like anything else on the site, and inexplicably the main
site navigation has been reimplemented from being a sidebar to the left to
instead being across the top of the page and half the items are removed. The
next habits to develop are to be trigger happy in committing things to version
control so that it's easy to roll back, to read every single suggested diff
very careful, and to not bother with AI at all if the basic functionality of
what I want already exists in a similar way. It's easier to just copy
the "New Person" page and make the required field changes than it is to try
and prompt engineer my way to the desired end state.
My observation on the state of "Vibe Coding" right now is it is truly at it's
best when the AI coding agent is able to take an incredible amount of license
in terms of implementation and has very few constraints on what and how things
need to be done. As things grow though there is less room for that creative
freedom. Nobody wants every page of a site to be an ecclectic mess of different
design choices (well, except for AWS 😝). Internal consistency both in terms
of coding implementation choices and end user experience become more important
than simply having it "done". It felt like a wrestling match to get things to
play nice as the constraints grew. The need to constantly instruct it to not
do more than I asked (don't change the navigation! I only want to change the
animation on a form button) became tiresome.
All that said, it's still an amazing improvement in productivity and one I'm
confident is now a permanent part of my workflow. It's also incredible to think
that this is the state of things now, this glimpse of capabilities is the worst
it will be, things will only continue to improve. Whatever frustrations I have
now will continue to be solved and disappear over time.
## Image Generation
Image generation is probably one of _the_ most ubiquitous AI use cases at this
point. It's certainly difficult to escape it, and this post is no exception.
I've been happily using [DiffusionBee](https://diffusionbee.com) on my Mac for
a couple of years now. I've played with Midjourney and being able to observe the
prompts other people use is educational but I just don't love the whole UX
around sitting in Discord to produce images. I prefer being able to run the
process locally, switch out models as required, tweak the parameters depending
on the speed/variations/quality I need to move things forward, etc.
As I called out earlier, what has changed over the past week is the way I find
inspiration for the prompts. I've been leaning on Claude heavily to either
generate or expand on my own ideas. Then I've been feeding multiple prompts
into a queue in DiffusionBee and letting it run in the background while I
finish writing things up. It produces a dozen or so variations for each prompt,
and then I pick one to upscale and add to my social posts, blog post, whatever.
Similar to the observation with coding agents, this stuff really shines when
there's a huge amount of scope for letting the model be creative and when there
isn't really much in terms of an objective definition of "correct". Where things
struggle is when you can look at a picture and quickly observe things that are
wrong. Hands and fingers on people are still often a challenge (some models
being better than others), I'm also not using it to generate anything with
writing on it. Sometimes the system will take it upon itself to smatter writing
over things, and it's always non-sensical. Occassionally that's fine because
it is an irrelevant enough detail to be overlooked. A lot of the time though
it turns out to be an unwelcome distraction and the image isn't usable.
## What's next?
So what's next? I've no idea! I've installed a time tracking app to help me
see how and where I'm spending my time. From there I'm hopeful I'll spot some
more low-hanging fruit where a slight change in my workflow rapidly
increases either the speed or quality of what I can achieve.
Though I'm not sure how much more there is to optimise. I feel like there's a
class of work where there's value in the process of doing the actual work. And
others where I need to be certain the outputs are correct and up to my standard,
but verifying that takes the same amount of time as doing the actual work.
I guess we'll see what happens. 🤖
## Nim: Concatenating strings
Concatenating strings is one of those topics where for any given language there's a nearly infinite way to achieve the end result. Though typically there will be one or two approaches that are deemed more idiomatic. Here's a couple of the more common approaches within Nim
```nim
var a = "This"
a.add(" is a string")
echo "a is now: ", a
# a is now: This is a string
var
b = "This"
c " is a string"
echo "concat: ", b & c
# concat: This is a string
```
You can also import `strutils` to use some functions that will look familiar from other languages:
```nim
# calling as a method on an array:
import strutils
echo @["a", "b", "c"].join(" ")
# a b c
# or as a function:
echo join(@["a", "b", "c"], "xx")
# axxbxxc
```
## Investment Flashback - Stackshare
After the IPO at HashiCorp (where I used to work), quite a few of my colleagues decided they wanted to explore angel investing. I've slowly built up quite a few private investment over the years and so have had quite a few informal chats over the past year about how I got into it, how I've approached it, what's worked, etc.. It's also proven to be a worthwhile moment of reflection for myself.
So before I talk about what's next for me, I thought I might take a few moments to talk about what a bunch of other interesting people have been up to over the past decade. People I've been witnessing to various degrees from the sidelines. It's tough out there for a lot of people right now and I'm hoping this turns out to be useful for at least one person. A reminder to other founders that success rarely happens overnight, and the many years of effort get hidden by the outcome. Maybe it's a new perspective of what at least some investors consider before writing a cheque. If you've recently lost your job, maybe you find a new interesting company in this series that is hiring. If you've lost some of your team maybe you discover a company that can help you be more efficient and solve a problems. Or if you're considering starting with angel investing yourself maybe this convinces you that it doesn't require much more than some spare capital that you're happy to risk and a willingness to say yes to someone. Being a _successful_ investor no doubt requires more than that. Getting started is just money and a yes though.
So over the next few posts I'm going to go through each of the investments in my portfolio that are still in play. They're a mix of direct investments where the founder and I have had a back and forth to make a deal happen, some syndicates SPVs where someone else has led and I've joined in as part of their investment vehicle, and a couple through a crowdfunding-style platform where I've literally never spoken to the founders and just liked the idea of being invested in the product.
I'll post them in no particular order, except for this one. I figured it made the most sense to go back to where it all started.
## Stackshare
I was incredibly fortunate to join Heroku at a really exciting time in their growth. The team were just amazing, to date the most inspiring group of people I've had the pleasure to work with. It was a heady mix of believing anything was possible while having the focussed execution to make it happen in an intentional and methodical way.
But in the years after the acquisition things slowly changed. It still felt like anything was _possible_, but I felt a growing sense that what we'd do in _reality_ was much more Salesforce-centric. It was a perfectly reasonable strategy, it just wasn't one I was personally excited by. And so in early 2014 I handed in my notice.
Having spent most of my time there on the Add-ons team meant I'd spent years thinking about the experience on most of the things that weren't Heroku. Adding a database, logging service, caching layer, SMS, whatever was such a low friction experience. I wanted _all_ apps to have that, not just apps running on Heroku. It also seemed like a pretty good business to be in if you could make it work: it's not an especially tough technical challenge (it sets a configuration value for each service you add) but Heroku took 30% of revenue for that service, in-perpetuity. A clip of revenue for a one-time introduction is pretty nice!
I also knew from watching so many other marketplaces try to challenge us and subsequently fail that it's not as simple as "just build a marketplace", people need some other reason to be using your product. The marketplace should be an emergent property solving some other problem. For Heroku it was that you couldn't run these other things yourself on a dyno. And at some point, quite early in development, you inevitably needed a database or logging or something. And so you found the marketplace.
So I knew if I was going to build a generalised marketplace of developer services I couldn't actually build a marketplace, I needed to solve some other problem and let the marketplace emerge.
### Solving the discovery problem
I'd spent years building out what was, at the time, the most successful marketplace of it's type. I knew what worked and what didn't. What people needed. One of the unsolved challenges with the Heroku marketplace was discovering what add-ons existed, and more importantly which one you should use. When there's only a dozen products on offer it's fine to just have a list. As they grow you need categorisation. Eventually that's not enough though. There's five different companies offering Redis as a service, with identical features and comparable price points... which one should I use? If there's an obvious and correct answer to that then why are the other four even offered to people? Why are you letting them make sub-optimal decisions for their infrastructure? What services _should_ I be using? Which ones are most appropriate for my stack? Which ones play nicely with the things I'm already using?
We'd run various experiments of limited success on this over the years, and while I wanted to do more I knew it was one of those areas where the business wanted to focus their energy in other areas. Which meant I knew it was a safe place for me to try and get a foothold. _If_ I could build out a compelling product that helped developers make better decisions about what tools they could or should be using, _then_ I could at in a single click "add this to my app" workflow that gave the Heroku Add-ons like experience.
And I figured I was pretty uniquely positioned to make this happen. There's only a few people who'd successfully done anything like this to date, and the rest of them were still at Heroku. Unfortunately I had a non-compete clause and so needed to bide my time (12 months to be precise) before I did anything that would be such a direct competitor to anything Heroku was doing. That was fine, we were moving our lives and new family from the US back to Australia anyway so I had time. While almost all of our belongings were in a shipping container slowly crossing the Pacific, we spent a few months traveling Europe. My mind constantly wandering to how I was going to tackle this challenge, what I needed to build, what was important to do differently.
### The competitive landscape
In the midsts of all that dreaming I started looking for products, either still functioning or now defunt, that were on the periphery of what I wanted to do. In the process I came across a neat looking product that had already implemented some of the early ideas I had. It looked quite slick too! I reached out to the founder to have a chat. Given my experience at Heroku, they were really excited by the prospect. For my part at the time this was entirely an attempt to understand who I might be competing with when I launched a similar thing. At the time the product was called LeanStack, but people know what it's become now as [StackShare](https://www.stackshare.io)
I still remember that first call with Yonas. With a sleeping newborn on our bed, I'd been kicked out into the hallway to chat with him so I didn't wake up the baby. I started the call smug and full of
confidence about how uniquely smart I was about this whole domain, and I ended it humbled and questioning everything I was doing. Not because it was confrontational or aggressive, far from it. Yonas has been one of the most humble, generous, and personable founders I've worked with. Over the course of that chat though it became clear I didn't know shit. Or at least nothing special. He'd worked it all out already. The mistakes we'd made at Heroku, he'd spotted them to. The bits that were missing he knew they needed to be filled. Whatsmore, he had a significant headstart and I already liked the visual aethetic of what he'd built better than what I had in my own head.
### Coming clean
Yonas and I spoke a couple of times over the coming days and it became clear that if I thought this was a problem worth solving it didn't make much sense for both of us to be doing it. We we're already on the same page with so much stuff that we'd end up building near replicas of each other's stuff. I came clean about my original intentions. Rather than worrying about having to setup a company, hire some engineers, and start building some things when I got back to Australia why don't I just give him some money instead and extend my holiday? A signed term sheet later and I'd suddenly made my first angel investment.
### Problems & Solutions are rarely what they first appear
There's a lot I've learnt and continue to learn from this first investment in Yonas. For a start and on reflection, it most definitely was an investment _in Yonas_. It might have started with a particular problem I wanted to solve. I probably even told myself at the time that's what I was investing in. However the reason things even progressed to that point was that first phone call. Where I suddenly realised he was so much smarter than me. He'd worked it all out without the advantages I had from my position inside Heroku. The actual cheque itself was an admission, because he was smarter, he'd be a better steward of that money than I would.
I said earlier that success rarely happens overnight, and so it's true here. It's been fascinating to watch the evolution of both StackShare and the market around it. If there was a window for my "generalised developer marketplace with Heroku Add-ons like experience" to exist I think it's long closed now, or only availble to just a handful of encumbents to pursue successfully. Meanwhile StackShare has uncovered those whole other problem I'd never recognised even though I've been subject to it myself on multiple occassions: when you've got multiple teams the question of "what tool should I use?" can also be a version of "what tool are we _already_ using to solve this problem?". Companies inadvertently add new tools or approaches to solve problems they'd already solved elsewhere. Standardisation and communication is a real problem. It becomes a huge mess pretty quickly. A huge expensive mess because the extra complexity, inefficiencies, and wasted extra licensing all quickly add up at scale (also, if this all resonates you should check out [StackShare Enterprise](https://stackshare.io/enterprise). It's free to sign up!). All this is stuff I simultaneously knew but overlooked back then.
Returns aren't always financial. I hope for the entire StackShare team's sake that at some point in their future they have a liquidity event. For me though I already consider my investment in Yonas to have had a very high positive return. Somewhere between three to five of my next investments were from direct referrals by Yonas. Either founders he thought I should speak to, or founders who'd discovered StackShare and reached out to him asking for an intro to their investors. Given the nature of what StackShare does, and Yonas' immense ability to network, there's been more than a few occassions where he's been able to open a door for me into another company I wanted to speak to. Most impactful of all though has been the career credibility it's given me over the years. Being an "angel investor" requires nothing my than money and to say yes. Again, that doesn't mean you'll be successful! It's opened so many doors and opportunities for me though. There's no chance AWS would have wanted me to join their Startup Team without having that experience on my CV. There's so many doors that have opened purely on the basic of people seeing me as an "angel investor". It's not a real job! It just means I can spend money. I sometimes wonder if it'd be a cheaper way to buy credibility that getting a university degree.
## Nim: Data Structures
More complex data structures can be defined via a `type` statement:
```nim
type
State = enum
Victoria, NewSouthWales, Queensland, Tasmania, SouthAustralia, NorthernTerritory, WesternAustralia, ACT
var home = Victoria
echo home
# Victoria
echo typeof home
# State
```
Arrays in Nim are of fixed length with their size specificed at compile time. Sequences are like arrays, but have a dynamic length. They can be constructed by the array constructor `[]` in conjunction with the array to sequence operator `@`.
```nim
var
x: seq[int] # a reference to a sequence of integers
x = @[1, 2, 3, 4, 5, 6]
```
There's also openarrays, though they can only be used as parameters:
```nim
proc openArraySize(oa: openArray[string]): int =
oa.len
```
Creating a custom object is done by defining a new type, with a type of `object`:
```nim
type
Person = object
name: string
age: int
var person1 = Person(name: "Peter", age: 30)
echo person1.name # "Peter"
echo person1.age # 30
```
Mark fields that should be visible from outside the defining module with `*`.
```nim
type
Person* = object # the type is visible from other modules
name*: string # the field of this type is visible from other modules
age*: int
```
A similar but alternate approach for object-like behaviour is to use tuples:
```nim
type
Person = tuple[name: string, age: int]
var
person: Person
person = (name: "Peter", age: 30)
```
## The industrial revolution for knowledge work has begun
In a cramped London workshop in the 1800s, a team of artisans bends over workbenches, crafting every component of a chair by hand. Each piece is a testament to skill and care — but also time, inconsistency, and cost. Then the assembly line arrives. At first, it's met with skepticism, even hostility. But soon, production scales. Quality stabilises. Entry-level work is redefined.
Fast-forward to today. My Brompton bike, still hand-assembled in London, is the child of that transformation. It's a symbol of what happens when craftsmanship meets repeatability. When thoughtful design embraces industrial process.
Now, the same forces are reshaping knowledge work.
## The AI Revolution Isn't Replacing Workers — It's Raising the Floor
When we talk about AI and jobs, the loudest conversations are usually about what's being lost. But that misses the more interesting shift: what's being standardised.
Much like how the industrial revolution gave us consistent, affordable goods, AI is setting a new baseline for what "good enough" looks like in knowledge work. Entry-level research? Automated. First-draft emails? Generated in seconds. Scheduling, summarising, prioritising? Quietly handled in the background.
The implication isn't that knowledge workers are obsolete. It's that the floor is rising. Fast. If your value was in knowing where to look or how to format a response, AI's got that now. But just as assembly lines didn't kill design, AI won't kill thinking. It will, however, make the shape of "junior" roles unrecognisable.
## The Entry-Level Isn't Disappearing—It's Evolving
It's tempting to say AI is "eliminating" junior roles. But that's not quite right. What's really happening is more nuanced: entry-level work still exists. But the expectations for what a junior person can accomplish are changing.
Think back to the factory floor during the industrial revolution. The artisan who once hand-carved each chair component didn't transition into an assembly line worker. They were often displaced by machines that could do in minutes what once took hours. But the roles that followed weren't devoid of skill, they simply required different kinds of mastery.
Instead of intimate knowledge of wood grain and chisel angles, workers needed fluency in timing, tooling, coordination, and safety. The expectation wasn't to create something wholly from scratch—it was to contribute reliably to a larger system. Precision and productivity became the new markers of competence.
What emerged was a new layer of specialisation: the machinist, the line supervisor, the tool-and-die maker. Each role demanded awareness not of the material, but of the system — how to operate within it, maintain it, and improve it. These were skilled roles, just not the same kind of skill.
Fast-forward to today. An entry-level accountant isn't expected to track balances in a paper ledger or manually reconcile line items from boxes of receipts. They start in Excel or Xero. They build pivot tables. They debug formula errors. The baseline of competence has changed because the tools have changed.
This is exactly what's happening now with AI in knowledge work.
A junior researcher today might not be expected to gather source material manually, but they will need to verify AI-generated summaries. A new content marketer might not need to draft copy from scratch, but they'll be expected to assess tone, optimise prompts, and steer the output toward a strategic outcome.
The shape of early-career work is adapting. What used to be years of grinding through rote tasks is now replaced by an expectation to direct, critique, and refine AI outputs. That's not less work — it's different work. And it requires a different kind of readiness.
We're not eliminating entry-level roles. We're raising the baseline of what entry-level means.
And that's ok.
Every generation enters the workforce under a different set of assumptions and tools. The tools get better. The expectations shift. The foundational human skills — judgment, communication, initiative — stay essential. But how we demonstrate those skills evolves.
This is not a threat. It's a recalibration. And just like the craftsman became the machinist, and the bookkeeper became the spreadsheet analyst, the next generation of knowledge workers will start their journey with AI as a co-pilot, not a competitor.
## We're All Bromptons Now
I ride my Brompton because it's a fusion of legacy and modernity. Its folding frame is as clever as the day it was conceived, but the experience is unmistakably modern: seamless, efficient, delightful.
That's the model for the future of work. AI as the scaffolding that supports, elevates, and accelerates. Not a crutch. Not a threat. A quiet motor humming in the background while you focus on what makes your input uniquely human.
Craft still matters. Taste matters more. Judgment, decisiveness, ethics — these aren't replaced. They're exposed. Amplified. When AI handles the busywork, what's left is the why, not the how. And that's a far scarier thing to be held accountable for.
## The Next Shift Isn't Automation. It's Expectation.
The shift we're experiencing isn't about efficiency. It's about assumption. We're entering an era where the default is:
* Your research will be thorough
* Your writing will be clean
* Your thinking will be structured
Because AI will take care of all three. That's the new baseline.
And once the baseline rises, everything above it becomes even more valuable… but also harder to fake. In a world where everyone can write a decent report, insight becomes the differentiator. Where everyone can summarise the meeting, influence becomes the skill. Where everyone can manage their inbox, discernment becomes the edge.
This is not a call to panic. It's a call to recalibrate.
If you're early in your career: seek out projects where AI can't yet tread. Ambiguity, novelty, chaos — these are your proving grounds.
If you're mid-career: resist the temptation to coast. Delegate to AI, but audit it ruthlessly.
If you're leading others: build new scaffolding. Find ways to teach judgment without needing people to do the grunt work first.
The industrial revolution didn't just birth factories. It reshaped education, labor policy, urban design. We're due for a similar reckoning in knowledge work. Not because the work is going away. But because the assumptions underneath it already have.
## Nim: Executing external commands
One fairly unique aspect of executing commands that Nim has, that I've not experienced anywhere else, is `staticExec`. It allows a process to be executed at _compile_ time, and the results at that specific moment included in the build in some way. An example from the docs is to interrogate the current environment to tag the current git status and OS architectures:
```nim
// Runs at compile time
const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
"\nCompiled on " & staticExec("uname -v")
```
To execute a process and return the results to a variable:
```nim
let output = execProcess("nim", args=["c", "-r", "hellowworld.nim"])
```
If you just want the exit status:
```nim
import osproc
let output = execCmd("blerk")
echo output
# sh: blerk: not found
# 127
```
Or if you want exit status _and_ output:
```nim
import osproc
let (output, status) = execCmdEx("ls -a")
echo status
# 0
echo output
# . ..
```
If you need to get more control then `startProcess` is the lower level function to call. The `execProcess` function earlier was just a convenience function to make common usage simpler. Make sure to close the process when you're done:
```nim
import osproc, strutils, streams
let process = startProcess("some-interactive-process", args = ["-v"],
options = {poInteractive, poUsePath})
let (fromp, top) = (process.outputStream, process.inputStream)
top.write "repeat this please\n"
top.flush
echo fromp.readLine.toUpper
top.write "and this\n"
top.flush
echo fromp.readLine.toUpper
discard process.waitForExit
process.close
```
## Nim: Making a HTTP GET request
Get requests in Nim are fairly straight-forward:
```nim
import std/httpclient
var client = newHttpClient()
echo client.getContent("https://google.com")
```
Nim also supports async requests, so to do the same but asynchronously:
```nim
import std/[asyncdispatch, httpclient]
proc asyncProc(): Future[string] {.async.} =
var client = newAsyncHttpClient()
return await client.getContent("https://google.com")
echo waitFor asyncProc()
```
## Nim: How to make a HTTP POST request
To post to an endpoint simulating a `form-data` response:
```nim
var client = newHttpClient()
var data = newMultipartData()
data["first_name"] = "glenn"
data["last_name"] = "gillen
echo client.postContent("https://some.api", multipart=data)
```
If instead you need to post a JSON object:
```nim
import std/[httpclient, json]
let client = newHttpClient()
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
let body = %*{
"first_name": "glenn",
"last_name": "gillen
}
let response = client.request("http://some.api", httpMethod = HttpPost, body = $body)
echo response.status
```
## Nim: How to read environment variables
# Nim: How to read environment variables
Reading environment variables is a simple way to enable runtime configuration of an application or script. Within [Nim](https://nim-lang.org) there's two functions to get familiar with for reading values from the environment.
```nim
if getEnv("MY_VAR") == "1":
echo "Got it!"
else:
echo "Nope :("
```
This gets the value of `MV_VAR` (the equivalent of `$MY_VAR` from your shell). If the value is unset `getEnv` will return an empty string. Or, alternatively, you can provide a second argument to `getEnv` to specify the default value to us if your variable is unset:
```nim
if getEnv("MY_VAR", "is unset") == "is unset":
echo "Nope :("
```
If you need to differentiate between a truly unset value and one that has inherited the default value then there is `existsEnv`:
```nim
if existsEnv("MY_VAR"):
echo "This is set"
else:
echo "This is not set"
```
## Nim: How to build a HTTP server
Nim uses the [Nimble](https://github.com/nim-lang/nimble) package managet to bundle dependencies (it's included with Nim as of 0.15.0). Using `nimble` we can install the [`prologue`](https://github.com/planety/Prologue) server
```bash
$ nimble install prologue
```
and now implementing a basic web server is not to dissimilar to Sinatra, ExpressJS, or most other minimalist frameworks:
```nim
import prologue
proc hello*(ctx: Context) {.async.} =
resp "
Hello World!
"
let app = newApp()
app.get("/", hello)
app.run()
```
## Learning Nim
Here's each step in my [14-step plan for learning a new systems language](../learning-systems-languages/), as applied to [Nim lang](https://www.nim-lang.org/):
- [Read a value from an environment variable](environment-variables/)
- [Concatenate strings](concatenating-strings/)
- [Extract a substring from a larger string](substrings/)
- [Execute an external command](executing-external-commands/)
- [Create a data structure](data-structures/)
- [Make a HTTP GET request](http-get/)
- [Make a HTTP POST request (with parameters)](http-post/)
- [Parse a JSON string into a data structure](parse-json/)
- [Convert a data structure into a JSON string](to-json/)
- [Parse a JSON string with nested objects](parse-nested-json/)
- [Read a file from the file system](read-file/)
- [Write a file to the file system](write-file/)
- [Parse a URL into its constituent parts](parse-url/)
- [Respond to a HTTP request (i.e., build a minimal HTTP server)](http-server/)
## Nim: How to parse JSON
To parse a JSON string into an object, use the `json` import from the standard library:
```nim
import json
let jsonObject = """{"first_name": "glenn", "last_name": "gillen"}"""
let jsonArray = """[7, 8, 9]"""
let parsedObject = parseJson(jsonObject)
let parsedArray = parseJson(jsonArray)
```
This will create a series of nodes, on which you'll need to call `getStr()`, `getInt()`, `getFloat()`, or `getBool()` to retrieve the actual value:
```nim
ech parsedObject["first_name"].getStr() # glenn
echo parsedArray[1].getInt() # 8
```
If you've a nested JSON object Nim will automatically parse the nesting and you'll only need to call the `get*()` on the edge node:
```nim
import json
let jsonObject = """{
"names": {
"first": "glenn",
"last": "gillen"
}
}"""
let parsedObject = parseJson(jsonObject)
echo parsedObject["names"]["first"].getStr() # glenn
```
To parse into an object, you'll need to define an object that matches your expected structure:
```nim
import json
type
Person = object
first_name: string
last_name: string
let jsonObject = parseJson("""{"first_name": "glenn", "last_name": "gillen"}""")
let person = to(jsonObject, Person)
echo person.first_name # glenn
```
## Nim: How to parse nested JSON into objects
Nim will automatically handle recursive parsing of a JSON string into its constituent parts. If
it's a nested object with specific types it can handle that too:
```nim
import json
type
Address = object
line: string
state: string
country: string
Person = object
first_name: string
last_name: string
address: Address
let jsonObject = parseJson("""{
"first_name": "glenn",
"last_name": "gillen",
"address": {
"line": "123 Main St",
"state": "VIC",
"country": "AU"
}
}""")
let person = to(jsonObject, Person)
echo person.first_name # glenn
echo person.address.state # VIC
```
## Nim: How to parse a URL/URI
Parsing a URL into its contituent parts is possible via `uri` module in the stdlib:
```nim
import std/uri
let myUri = parseUri("https://alice:secrets@glenngillen.com:8080/foo?q=bar#baz")
echo myUri.scheme # https
echo myUri.username # alice
echo myUri.password # secrets
echo myUri.hostname # glenngillen.com
echo myUri.port # 8080
echo myUri.path # /foo
echo myUri.query # q=bar
echo myUri.anchor # baz
```
## Nim: How to read a file
Reading the entire file in as a string is a simple one-shot:
```nim
let contents = readFile("hello.txt")
echo contents # prints the entire file
```
If you want to read it incrementally then:
```nim
proc readMyFile() =
let f = open("hello.txt")
# Close the file object when you are done
defer: f.close()
let line = f.readLine()
echo line
readMyFile() # will only read the first line
```
## Nim: Finding & extracting substrings
Much like in Ruby, Nim treats strings as a sequence of chars. This means if you already know the positional value of the substring you want to access you can access it like an array (zero indexed):
```nim
var a = "Hello World!"
echo a[0]
# H
```
It also means you can supply a range to access more than a single char, having a slice returned:
```nim
var a = "Hello World!"
echo a[0 .. 4]
# Hello
```
In JavaScript/ECMAScript to work backwards from the end of the string means first determining the
length of the string, which is also possible in Nim by calling `len(str)`. However the recommended
way to do it is using the `^` operator which is interpreted by the compile as the same thing, e.g.:
```nim
var a = "Hello World!"
echo a[0 .. ^1]
# Hello World!
# i.e., the whole strong
echo a[0 .. ^2]
# Hello World
echo a[^6 .. ^2]
# World
```
To check if a string contains a substring:
```nim
import strutils
var str = "Hello World!"
echo str.contains("World")
# true
```
To find the positional location of a substring:
```nim
import strutils
var str = "Hello World!"
echo str.find("World")
# 6
```
More advanced matching is possible via the [`std/strscans`](https://nim-lang.org/docs/strscans.html#scanf.m%2Cstring%2Cstatic%5Bstring%5D%2Cvarargs%5Btyped%5D) import. This is handly when you might ordinarily want to reach for regex, but want something a little more readable and expressive of intent. For example, this will extract integers that match the `(int, int, int)` format:
```nim
const input = "(1,2,4)"
var x, y, z: int
if scanf(input, "($i,$i,$i)", x, y, z):
echo "matches and x is ", x, " y is ", y, " z is ", z
# matches and x is 1 y is 2 z is 4
```
Rather than instantiating those variables it's also possible to use `scanTuple` to return a tuple of values:
```nim
let (success, year, month, day, time) = scanTuple("1000-01-01 00:00:00", "$i-$i-$i$s$+")
if success:
assert year == 1000
assert month == 1
assert day == 1
assert time == "00:00:00"
```
If you do need the power of regular expressions the [`std/re`](https://nim-lang.org/docs/re.html) gives you access to the functions you'd expect:
```nim
import std/re
var matches: array[2, string]
if match("abcdefg", re"c(d)ef(g)", matches, 2):
echo matches
# ["d", "g"]
```
## Nim: How to write a file
Writing a file is as straight-forward as [reading one](../read-file/):
```nim
let text = "Hello World!"
writeFile("hello.txt", text)
```
Writing each line incrementally is also similar:
```nim
proc writeHello() =
let lines = ["Hello", "World", "!!"]
let f = open("hello.txt", fmWrite) # fmWrite is the file mode constant
defer: f.close()
for line in lines:
f.writeLine(line)
writeHello()
```
## Nim: Object to JSON
Turning a data structure into JSON is incredibly easy in Nim:
```nim
import json
type
Person = object
first: string
last: string
var p = Person(first: "Glenn", last: "Gillen")
echo(%p)
```
Where the `%` macro/operator is shorthand from the `json` module for converting to JSON. There's also
the `%*` macro which can be used for assigning an object directly into a JSON data structure:
```nim
import json
let obj = %* { "first_name": "glenn", "last_name": "gillen" }
echo $obj
```
## Rust: Concatenating strings
Another seemingly simple task, another trip down the Rust rabbit-hole to actually understand what's happening. It turns out there's two similar but different types of string: `String` and `str`. A gross oversimplification that I'm sure will make rustaceans cringe, but is sufficient for my hours-long understanding of how to use Rust: `str` is an immutable type, which you access via a reference (e.g. `&str`). A `String` is for when you know you'll want to own and mutate the string.
With that, we've already seen one way to concatenate string values in our [previous exploration of reading and outputting environment variables](../environment-variables/). The `println!` syntax is using the `format!` macro's syntax:
```rust
fn main() {
let a = "Hello";
let b = "World!";
let c = format!("{} {}", a, b);
println!("{}", c);
}
```
Next up is to take advantage of that mutable `String`, by declaring a mutable variale and then pushing a new value onto the end of it:
```rust
fn main() {
let mut a = "Hello".to_string();
let b = " World!";
a.push_str(&b);
println!("{}", a);
}
```
We could also use the `concat()` method on an array:
```rust
fn main() {
let a = "Hello";
let b = "World!";
let c = [a, " ", b].concat();
println!("{}", c);
}
```
Or `join()`:
```rust
fn main() {
let a = "Hello";
let b = "World!";
let c = [a, b].join(" ");
println!("{}", c);
}
```
## Rust: Data structures
Rust has a real focus on memory-efficiency as well as memory and thread safety. Which means you can't quite play as fast and loose with variables as you might in a language like Ruby. We've already seen a glimpse of that with the way we had to work with strings. We're going to encounter more of it now with the various data structures we play with.
```rust
fn main() {
let arr = [1, 2, 3, 4];
println!("{}", arr[2]); // 3, because arrays are zero-indexed
}
```
That array is immutable though. We can't add new items. We can't change anything within it. We could do the latter by adding the `mut` keyword to the definition but that's only going to be valuable in limited circumstances.
Enter Vectors! Arrays, but growable.
```rust
fn main() {
let mut arr = vec![1, 2, 3];
arr.push(4);
println!("arr is now {} items", arr.len()); // Prints 4
}
```
Next up, tuples:
```rust
fn main() {
let tuple = ("Glenn", 40);
println!("{:?}", tuple);
println!("{}", name);
println!("{}", age);
}
```
Just pairs (or more) of values that you can assign to a variable. You can even have tuples of tuples. Accessing the values is a little trickier as they need to be destructured into it's constituent parts. You'll also see some experimentation with `:?` formatter to print the debug output.
Now to some more complicated setups:
```rust
#[derive(Debug)]
struct Person {
name: String,
age: u8,
}
fn main() {
let name = String::from("Glenn");
let age = 40;
let glenn = Person { name, age };
println!("{:?}", glenn);
println!("{}", glenn.name);
println!("{}", glenn.age);
}
```
A struct! It's actually quite familiar to many other languages. Note the introduction of the `#[]` compiler hint to tell it how to handle an attempt to print debug info of this struct. Instantiation expects values in the same order if you use the field init shorthand. Or you can be more explicit by providing `field: value` definition pairs.
## How I learn new systems languages
I still dabble in some coding from time-to-time. A personal project here and there, maybe spiking out a proof of concept for a new idea. But it's been a long time since it's what I was actually paid to do. Even back when I was paid to do it as a core part of my role I skewed more towards systems programming. CLI tooling, services, and the barest of web/frontend competencies. I've always enjoyed playing with front-end tech like React, Elm, and Svelte because it's pushed me in uncomfortable ways. Forced me to wrap my head around new concepts. And be ok with the fact that I'm kinda rubbish at designing exceptional web-based experiences.
Systems langauges, and the jobs I need them for, that's a different story. My relationship has always felt more transactional. I have a job I need to get done. It's likely just the latest version of a problem I've already solved multiple times over the previous decades just with some new nuance for the current context. I want the quickest and easiest solution, and the one that is easiest to maintain. Quickest and easiest is most often a proxy for familiarity. Whatever language I'm most comfortable with is almost always going to win out over the combined cost of learning a new language and then solving the problem. To keep myself sharp I used to regularly join hackathons and use it as an excuse to try and pick up a new language. I'd often spend a weekend reading documentation and fundamentals but ultimately have nothing to show for it. One day at such an event someone I really respected seemed puzzled by my approach, "I dunno man. I have a rule: don't learn a new language on a new problem. There's too many unknowns when things go wrong. Did I create a bug? Is my design wrong? Is it a quirk in the langauge? Solve new problems with languages you know. Learn new languages with problems you already know the answer to".
He was right.
Picking up a new systems language has been so much easier since then. As I said, so much of the day job is new versions of a problem I've seen before. Taking his advice of solving a previous problem meant I could build myself a mini curriculum to learn any new language. Learn these fundamental tasks and I can be moderately productive in a new language. My time can then be spent increasing familiarity with the standard library and larger ecosystem. Focus on the idiomatic ways of doing things in that language. Basically, focus on the unique parts that make the language different and interesting.
What I've learned to love about this approach is that the goals are objective outcomes. It's not concepts that typically have hundreds of slightly nuanced and context-dependent decisions to make like "learn the different approaches of modifying control flow". It's "read an environment variable". For some languages that almost trivially easy (e.g., Ruby). It might only take a few minutes to achieve. And that's ok! You can call it done for the day and come back tomorrow, or continue on to the next things. For other languages that simple task might
already be enough for force you to understand the idiomatic approaches to error handling (e.g. Go). All these things combined implicitly require you to start layering in the more foundational 101-level requirements of variable assignments, defining types, importing modules/stdlib, etc.
And before you know it, you've enough competency to start using it as part of your day-to-day toolset. Little scripts to scratch an itch. Tools to solve problems. But _new_ (to you) problems. Problems you didn't already know the answer to.
## Glenn's 14 step guide to learning a new language
This isn't the totally fool-proof plan for everyone. This is _my_ plan. I'd recommend looking back over your own work for the past year or two and work out the most fundamental moving parts. Here's mine:
- Read a value from an environment variable
- Concatenate strings
- Extract a substring from a larger string
- Execute an external command
- Create a data structure (e.g. a class, type, interface, etc.)
- Make a HTTP GET request
- Make a HTTP POST request (with parameters)
- Parse a JSON string into a data structure
- Convert a data structure into a JSON string
- Parse a JSON string with nested objects
- Read a file from the file system
- Write a file to the file system
- Parse a URL into its constituent parts
- Respond to a HTTP request (i.e., build a minimal HTTP server)
## Pulling it together
And with that, it's time to give you an example of how it works by learning a new language in the open. I've been wanting to learn [Nim](https://nim-lang.org) for a while now. And so it begins...
- [Learning Nim](../learning-nim/)
- [Learning Rust](../learning-rust/)
## Rust: Executing external commands
As with most languages, there's multiple ways to execute or shell out to a process. The most straight-forward (IMO) is to create a new `Command` and call `output()`:
```rust
use std::process::Command;
fn main() {
let output = Command::new("ls")
.arg("-l")
.arg("-a")
.output()
.expect("failed to execute process");
println!("status: {}", output.status);
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
}
```
As we've discussed in the [earlier editions](../environment-variables), Rust gives you the tooling to code defensively and handle error cases properly. Or it'll let you be super lazy for the sake of demoing how something works. The latest version of being lazy for us is using `expect()`. Think of it as the compliment to `unwrap()`. Where `unwrap()` took a `Result` and effectively said "let's just assume everything worked, I only want the output of the success" what `expect()` is saying is "if something goes wrong, raise an error of 'failed to execute process'... but really I still only just want the output". The result of `output()` is a `Result`, and you should handle it (properly) as such.
But for now, we're taking a shortcut to show what you can do with the succesful result which we've assigned to `output`. As you'll see from the `println!()` statements it gives us access to the exit code (`status`) as well as stdout and stderr.If all you need is the exit code there is actually an alternative method to call in [`status()`](https://doc.rust-lang.org/std/process/struct.Command.html#method.status).
For a little control of things you can instead call [`spawn()`](https://doc.rust-lang.org/std/process/struct.Command.html#method.status) and get a reference to the child process:
```rust
use std::process::{Command, Stdio};
use std::io::{BufRead, BufReader, Error, ErrorKind};
fn main() -> Result<(), Error> {
let stdout = Command::new("ls")
.arg("-l")
.arg("-a")
.stdout(Stdio::piped())
.spawn()?
.stdout
.ok_or_else(|| Error::new(ErrorKind::Other,"Could not capture standard output."))?;
let reader = BufReader::new(stdout);
reader
.lines()
.filter_map(|line| line.ok())
.filter(|line| line.find(" .").is_some())
.for_each(|line| println!("{}", line));
Ok(())
}
```
Some more new Rust additions in this approach! We've updated `main()` to be able to return a result. We've passed in a new pipe `Stdio::piped()` into our command so we can connect to the child process. We're using the `?` operator on the call to `spawn()` to give us just the success result. We're then accessing `stdout` property and calling `ok_or_else()` to have it throw an `Error` if we don't have access to stdout for some reasons. Whew! That's a lot... in surprisingly little code all things considered.
Next... we're putting our new stdout pipe into a buffer reader, and then we read. Filtering for lines with values, then filtering further for lines looking for any that contain ` .`, where `is_some()` is returning true where the result has a value (though we don't care to check exactly what the value is). Finally we loop over all of the matching lines and print them out.
A call to `ls` probably isn't the most valuable demonstration this though it's enough to see all of the moving parts in action. You can take the same approach with a writer feeding into stdin.
## Rust: Making a HTTP POST request (with parameters)
This one proved to be a little easier given we'd established all of the foundations with [a get request](../http-get/):
```rust
use hyper::{Body, Method, Client, Request};
use hyper::body::HttpBody as _;
use tokio::io::{stdout, AsyncWriteExt as _};
use url::form_urlencoded;
#[tokio::main]
async fn main() -> Result<(), Box> {
let params = vec![("foo", "bar")];
let body = form_urlencoded::serialize(params.into_iter());
let client = Client::new();
let req = Request::builder()
.method(Method::POST)
.uri("http://httpbin.org/post")
.body(body)?;
let mut resp = client.request(req).await?;
println!("Response: {}", resp.status());
while let Some(chunk) = resp.body_mut().data().await {
stdout().write_all(&chunk?).await?;
}
Ok(())
}
```
We've had to unravel the previous `get()` request into two parts now: 1) building out the request objects and then 2) passing the request into the client to execute it. Adding on to that `Request::builder` is how and where we'd add additional headers or otherwise manipulate the request before sending it.
## Rust: How to read environment variables
This is one of the examples I 😍 about this approach to [learning a new systems language](../learning-systems-languages/). It's already forcing me down the rabbit-hole of understanding so much about what makes Rust interesting. Here's the simplest possible way I could find to get the value of an environment var:
```rust
use std::env;
fn main() {
let key = "HOME";
let val = env::var(key).unwrap();
println!("{}: {:?}", key, val); # /Users/glenngillen
}
```
Import the relevant `stdlib`, call the `var` function, all pretty standard so far. But there's that curious `unwrap()` call on there too. What's that about? Well it turns out most Rust functions return a [`Result` type](https://doc.rust-lang.org/std/result/) (or similar `Option` type) rather than just a value. Much like in golang (though there it's more of an idiom to return a tuple of values rather than a type of its own) you're expected to explicitly handle both the success and failure case and not just assume everything is always successful.
`unwrap()` is kinda cheating here, and asking Rust to just give me the value from the success result (i.e., _"unwrap"_ the result type to give me the value). So what happens if we try to access an environment variable that is not set:
```rust
use std::env;
fn main() {
let key = "NOPE";
let val = env::var(key).unwrap(); # This will panic
println!("{}: {:?}", key, val); # We'll never get here
}
```
we get this:
```terminal
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 1.21s
Running `target/debug/playground`
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: NotPresent', src/main.rs:15:29
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
ruh-roh! We can't have our whole app panic just because an environment variable is not set!
But before we get to that a brief diversion: while calling `unwrap()` in this particular example is poor form, there's plenty of scenarios where it's not just acceptable but would produce simpler and more maintainable code. So much so that there's an operator to make achieving a similar result easier: `?`. _Similar_ is doing a lot of heavy lifting here, as I couldn't just replace the use of `unwrap()` with `?` in this example. You can only use `?` on `Result` or `Option` types, and it also returns an `Err` automatically too. Which means the function calling it needs to be capable of returning an error result, which our current `main()` does not. Thankfully modern versions of Rust (anything >= 1.26.0) allow `main()` to return a result. So a quick update of our example to return a result, including the error type from a failed variable lookup:
```rust
use std::env;
fn main() -> Result<(), std::env::VarError> {
let key = "NOPE";
let val = env::var(key)?;
println!("{}: {:?}", key, val);
Ok(())
}
```
All that was just to highlight how to use `?` instead of `unwrap()`. Given we're going to be handling the error case explicitly we'll revert back to the original `main()` definition for the rest of that. And with that, back to a more robust way of dealing with the scenario that an environment variable is not set.
Rust has the `match` keyword which can be used as a form of flow control like a `switch` statement. You can also use it as a way to handle the success or error cases for a result, with `Ok()` and `Err()` respectively:
```rust
use std::env;
fn main() {
let key = "NONE";
match env::var(key) {
Ok(val) => println!("{}: {:?}", key, val),
Err(e) => println!("couldn't interpret {}: {}", key, e),
}
}
```
Here you can see that we're passing the `Result` from our call to `env::var()` into `match`. Then there's two branches. For the `Ok()` result, take the `val` and pass it into the `println!` call we had earlier. Now for the `Err()` case we catch the error raised (`e`) and print out more descriptive output of what went wrong. The result now is:
```terminal
couldn't interpret NONE: environment variable not found
```
## Rust: Making a HTTP GET request
Wwooooowweeeee. I see a theme developing here! Even the seemingly most trivial of tasks if forcing me further down the rabbithole of understanding the Rust-way of doing things. Not that it's necessarily a bad thing, over the long-term, but it definitely front loads _a lot_ of the learning. I'm not even half-way through my usual [14-step process for learning a new language](../learning-systems-languages/) and I've already spent twice as long getting acquainted with Rust as I did Nim.
Today's foray started with having to get familiar with `cargo` and understanding how dependency management worked. We're going to use the [`hyper`](https://hyper.rs) package, which also recommends using [`tokio`](https://github.com/tokio-rs/tokio) for building asynchronous apps. Time to create a `Cargo.toml` file to define our dependencies:
```toml
[dependencies]
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }
```
Trying to get the basic example working (we'll get there soon!) threw up another error:
```
error[E0670]: `async fn` is not permitted in Rust 2015
--> src/main.rs:4:1
|
4 | async fn main() -> Result<(), Box> {
| ^^^^^ to use `async fn`, switch to Rust 2018 or later
```
And so more digging into Rust editions and more updates to `Cargo.toml`, which now looks like:
```toml
[package]
name = 'http-get'
version = '0.0.1'
edition = "2021"
publish = false
[dependencies]
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }
```
Turns out though that to use a specific edition via `rustc` means providing it via the `--edition=` flag.
Which now gets us to another Rust feature that I already love:
```terminal
error[E0752]: `main` function is not allowed to be `async`
--> src/main.rs:4:1
|
4 | async fn main() -> Result<(), Box> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` function is not allowed to be `async`
Some errors have detailed explanations: E0432, E0433, E0752.
For more information about an error, try `rustc --explain E0432`.
```
And sure enough, running `rustc --explain E0432` does indeed give a more detailed and useful explanation of what's wrong! 😍 When you're familiar with a language all of the "helpful" explanatory context can quickly just become noise that buries the actually useful detail. When you're unfamiliar with a particular error though an overly terse output can make it impossible to extract any actionable insight. This feels like such a great way to balance both needs: increase the signal:noise ratio while making additional context available if required.
But... the actual problem was continuing to try and use `rustc` for testing. `cargo run` is the way forward to have it use all of my `Cargo.toml` config.
And finally, we having a working example:
```rust
use hyper::Client;
use hyper::body::HttpBody as _;
use tokio::io::{stdout, AsyncWriteExt as _};
#[tokio::main]
async fn main() -> Result<(), Box> {
let client = Client::new();
let uri = "http://httpbin.org/ip".parse()?;
let mut resp = client.get(uri).await?;
println!("Response: {}", resp.status());
while let Some(chunk) = resp.body_mut().data().await {
stdout().write_all(&chunk?).await?;
}
Ok(())
}
```
The `#[tokio...]` bit is a macro that tells Rust to run the following code on the tokio runtime, which now allows us to define `main()` as being `async`. And within it we've now got access to `await` to make waiting for asynchronous responses easier.
The other notable edition to this code is the introduction of `Box` in the error path for the `Result` that `main()` now returns. This is another case where understanding the memory management of Rust becomes important and it's concepts around ownership and borrowing. By default, most variables/assignments end up on "the stack" which if a LIFO buffer. It's more efficient but it requires a higher degree of certainty around the size or type of data that will be stored in that memory location. The alternative is "the heap" which supports a little more dynamism that the stack, but it only returns a pointer and so is a degree or so removed in terms of the indirection it creates. But sometimes that's what you need. And when you do, `Box` is how you declare you want your reference to be on the heap and not the stack. Check out the Rust guide on [What is Ownership?](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html) for more details on the distinctions between the stack and heap.
## Rust: Create a basic web/HTTP server
For the web server we're going to use [the same HTTP library we used for making requests](../http-get), `Hyper`. The Hyper docs actually have a [really great guide for creating a server](https://hyper.rs/guides/server/hello-world/) which is exactly what this has been based off. Here's my version:
```rust
use hyper::{Body, Request, Response, Server, Method, StatusCode};
use hyper::service::{make_service_fn, service_fn};
async fn echo(req: Request) -> Result, hyper::Error> {
match (req.method(), req.uri().path()) {
// Serve some instructions at /
(&Method::GET, "/") => Ok(Response::new(Body::from(
"Welcome! Try a GET to /hello",
))),
(&Method::GET, "/hello") => Ok(Response::new(Body::from(
"World!",
))),
// Simply echo the body back to the client.
(&Method::POST, "/echo") => Ok(Response::new(req.into_body())),
// Return the 404 Not Found for other routes.
_ => {
let mut not_found = Response::default();
*not_found.status_mut() = StatusCode::NOT_FOUND;
Ok(not_found)
}
}
}
async fn shutdown_signal() {
// Wait for the CTRL+C signal
tokio::signal::ctrl_c()
.await
.expect("failed to install CTRL+C signal handler");
}
#[tokio::main]
async fn main() -> Result<(), Box> {
let addr = ([127, 0, 0, 1], 3000).into();
let service = make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(echo)) });
let server = Server::bind(&addr).serve(service);
println!("Listening on http://{}", addr);
let graceful = server.with_graceful_shutdown(shutdown_signal());
// Run this server for... forever!
if let Err(e) = graceful.await {
eprintln!("server error: {}", e);
}
Ok(())
}
```
## Learning Rust
Here's each step in my [14-step plan for learning a new systems language](../learning-systems-languages/), as applied to [Rust lang](https://www.rust-lang.org):
- [Read a value from an environment variable](environment-variables/)
- [Concatenate strings](concatenating-strings/)
- [Extract a substring from a larger string](substrings/)
- [Execute an external command](executing-external-commands/)
- [Create a data structure](data-structures/)
- [Make a HTTP GET request](http-get/)
- [Make a HTTP POST request (with parameters)](http-post/)
- [Parse a JSON string into a data structure](parse-json/)
- [Convert a data structure into a JSON string](to-json/)
- [Parse a JSON string with nested objects](parse-nested-json/)
- [Read a file from the file system](read-file/)
- [Write a file to the file system](write-file/)
- [Parse a URL into its constituent parts](parse-url/)
- [Respond to a HTTP request (i.e., build a minimal HTTP server)](http-server/)
## Rust: Parse nested JSON
Not a whole lot new to cover here that isn't already implied from the [previous parsing JSON lesson](../parse-json/). It works exactly as you'd expect. So here's a bonus shorthand way to parse an object into a JSON structure:
```rust
use serde_json::json;
fn main() {
let glenn = json!({
"name": "Glenn Gillen",
"age": 40,
"phones": [
"+61 448612567"
],
"address": {
"line1": "123 Main St",
"state": "VIC",
"country": "AU"
}
});
println!("first phone number: {}", glenn["phones"][0]);
println!("state: {}", glenn["address"]["state"]);
println!("{}", glenn.to_string());
}
```
## Rust: Parsing JSON
Enter the world of serializer/deserializers, or serdes. While you can build your own, it's probably no great suprise that Rust has one for JSON available out of the box:
```rust
use serde_json;
fn main() {
let data = r#"{
"first_name": "Glenn",
"last_name": "Gillen",
"age": 40
}"#;
let json: serde_json::Value = serde_json::from_str(data)
.expect("JSON was not well-formatted");
println!("{}", json)
}
```
We have to provide an explict type annotation here on the assignment to the `json` variable. The type of `serde_json::Value` is a convenient way to convert untyped JSON values by saying it can basically be [any of the 6 different possibilities a JSON value could represent](https://github.com/serde-rs/json#operating-on-untyped-json-values).
To parse into a structured object/type means first defining they struct. Then we use the `#[derive]` macro we [saw earlier for enabling debug output](../data-structures/), which we'll use again here, but this time we'll add the `Serialize` and `Deserialize` helpers. Finally we change the type annotation from the generic `serde_json::Value` to our desired type of `Person`:
```rust
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Serialize, Deserialize, Debug)]
struct Person {
first_name: String,
last_name: String,
age: u8,
}
fn main() {
let data = r#"{
"first_name": "Glenn",
"last_name": "Gillen",
"age": 40
}"#;
let p: Person = serde_json::from_str(data).expect("JSON was not well-formatted");
println!("{:?}", p)
}
```
## Rust: Parse a URL
Parsing a URL into its contituent parts is possible via `url` crate:
```rust
use url::Url;
fn main() {
let url = "https://alice:secrets@glenngillen.com:8080/foo?q=bar#baz";
let parts = Url::parse(url).expect("Unable to parse URL");
println!("{}", parts.scheme());
println!("{}", parts.username());
println!("{}", parts.password().expect("no password"));
println!("{}", parts.host_str().expect("no host"));
println!("{}", parts.port().expect("no port"));
println!("{}", parts.path());
println!("{}", parts.query().expect("no query"));
println!("{}", parts.fragment().expect("no fragment"));
}
```
## Rust: Read a file
There's a simple one-shot was to read a file in as a string:
```rust
use std::fs;
fn main() {
let s = fs::read_to_string("input.txt").expect("Unable to read file");
println!("as string: {}", s);
}
```
Or if you need to do the equivalent but as a vector of bytes:
```rust
use std::fs;
use std::str;
fn main() {
let d = fs::read("input.txt").expect("Unable to read file");
let data = str::from_utf8(&d).expect("Unable to convert bytes to string");
println!("as vector of bytes, length: {} as data: {}", d.len(), data);
}
```
To read a file incrementally you'll instead `open()` it and then iterate over the `lines()`:
```rust
use std::fs;
use std::io::{BufReader, BufRead};
fn main() {
let input = fs::File::open("input.txt").expect("Unable to open file");
let buffered = BufReader::new(input);
for line in buffered.lines() {
println!("line: {}", line.expect("error reading line"));
}
}
```
## Rust: Extracting substrings
As in Nim, Rust will let you access strings via positional location/slice:
```rust
fn main() {
let a = "Hello World!";
let b = &a[0..];
println!("{}", b); // Hello World!
let c = &a[0..1];
println!("{}", c); // H
let d = &a[..5];
println!("{}", d); // Hello
let e = &a[6..];
println!("{}", e); // World!
}
```
Though it's worth mentioning this is a slice of _bytes_, so unicode characters would take two. It's also not difficult to make this approach panic, so there's more than enough reasons to suggest it's probably not what you want to do most of the time.
A similar approach, but with the ability to better handle the error cases is to call `get()` instead of accessing the slice directly. In this example, for the sake of readability and showing like-for-like, I'm going to call `unwrap()` on each example. Better would be to [take the previous lessons on error handling](../environment-variables/) and apply them here:
```rust
fn main() {
let a = "Hello World!";
let b = a.get(0..).unwrap();
println!("{}", b); // Hello World!
let c = a.get(0..1).unwrap();
println!("{}", c); // H
let d = a.get(..5).unwrap();
println!("{}", d); // Hello
let e = a.get(6..).unwrap();
println!("{}", e); // World!
}
```
For finding a substring within a string, there are the `find()` and `rfind()` methods. The latter giving the result starting from the right (end) of the string:
```rust
fn main() {
let a = "Hello World!";
let b = a.find("World").unwrap();
println!("{}", b); // 6
}
```
If you're expecting to find multiple matches there is `matches()` and `match_indices()` which will return an iterator for going over results:
```rust
fn main() {
let a = "Hello World! Hello Rustaceans! Hello Everyone!";
for (idx, matched) in a.match_indices("Hello") {
println!("Found {} at index {}", matched, idx);
}
}
```
Would output:
```terminal
Found Hello at index 0
Found Hello at index 13
Found Hello at index 31
```
You could then use the index from this method to do whatever byte position based substring access you wanted.
## Rust: Convert a data structure into a JSON string
We've already done most of the heavy-lifting for turning a structure into JSON:
```rust
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Serialize, Deserialize)]
struct Person {
first_name: String,
last_name: String,
age: u8,
}
fn main() {
let first_name = String::from("Glenn");
let last_name = String::from("Gillen");
let age = 40;
let p = Person{ first_name, last_name, age };
let data = serde_json::to_string(&p).expect("Not a serializable type");
println!("{}", data)
}
```
Instantiate an instance of our `Person`, parse it into the serde via calling `to_string`. Done.
## Rust: Write a file
Again we've already done much of the heavy lifting in terms of learning from just working out how to [read a file](../read-file/).
```rust
use std::fs::File;
use std::io::{Write};
fn main() {
let mut output = File::create("output.txt").expect("Unable to open file");
write!(output, "Hello\nWorld\n!\n").expect("Unable to write");
}
```
The file is automatically closed once the reference is out of scope. I quick way to have more control around that is to surround it in braces `{}` so that the file is closed as soon as we've written to it:
```rust
use std::fs::File;
use std::io::{Write};
fn main() {
{
let mut output = File::create("output.txt").expect("Unable to open file");
write!(output, "Hello\nWorld\n!\n").expect("Unable to write");
}
}
```
Ordinarily this would be overkill. But it sets us up for the next addition, which is to show you how to open a file so you can append to it (rather than creating/truncating a file like the existing demo did):
```rust
use std::fs;
use std::io::{Write};
fn main() {
{
let mut output = fs::File::create("output.txt").expect("Unable to open file");
write!(output, "Hello\nWorld\n!\n").expect("Unable to write");
}
let mut file = fs::OpenOptions::new()
.write(true)
.append(true)
.open("output.txt")
.expect("Unable to open file");
write!(file, "and goodbye\n").expect("Unable to write");
}
```
## Learning web3
Following on from my recent series of try to expand on my familiarity of various systems languages, I've decided
it's time to broaden out into an area that's been on the periphery of my curiosity for a while: smart contracts,
decentralized finance, blockchain, and all things "web3".
I've been a bit of a blockchain skeptic for a while. I think in part because of the avalanche of founders I met
circa 2016/2017 pitching me terrible startup ideas for already solved problems… "but blockchain!" 🤦♂️. I've
also not been thrilled about the enviromental impacts of it. Not solving valuable, or even interesting, problems
while consuming huge amounts of energy in the process is a horrible combination. However…:
1. Despite decades (!!) of skepticism it's still here and at this point it seems more likely to be sticking around rather
that disappearing.
2. The complaining about the sustainabiltiy and chastising of those using it has done little to prevent people from using it. If anything it's probably created yet another divide in what feels like an increasingly divided world.
3. I've been lurking close enough to the periphery to see a number of interesting projects that might have people re-assess the energy cost (by reducing it significantly) and the value of what's possible.
4. If complaining hasn't been successful in improving things, maybe it's long overdue we pull up our sleeves and see if we can be part of the solution instead.
It's a sufficiently broad enough area I've decided to setup a completely separate site where I'll continue to build out content: [web3byexample.io](https://web3byexample.io/).
## Lionel Messi's left foot doesn't need AI
There's a story I love about Lionel Messi. You've probably heard it before—he's arguably the greatest footballer of all time, but here's the part that always stuck with me: the man has *one* foot.
Okay, that's an exaggeration. Technically, he has two. But he's so dominant with his left that defenders know exactly what he's going to do—and they still can't stop him. It's not that he's balanced. It's that he leans *all the way in* to what makes him brilliant.
That story came up again when I was reading *Nine Lies About Work* by Marcus Buckingham. One of the key takeaways from the book is this: **you don't need to fix all your gaps to be great.** In fact, the best performers usually have obvious weaknesses—they're just so ridiculously good in their zone of genius that it doesn't matter.
That hit home for me. I've spent years managing and coaching teams, and this idea—amplify strengths, don't obsess over gaps—is something I've seen play out over and over again.
But here's the thing. In a lot of companies, especially those with rigid career matrices, promotions are all about checking boxes. To get to the next level, you're expected to be *consistently good* across a broad set of skills. And if you're not? Well, here's the feedback: "You've got some gaps we need to work on."
Here's my take: sure, some gaps matter. You need a baseline level of competence in some areas to not be a bottleneck for the team. But beyond that? Trying to make everyone well-rounded often ends up making everyone average.
The book also draws a sharp distinction between *talent* and *strength*. That was a bit of a lightbulb moment for me.
- **Talent** is something you're naturally good at. It comes easy.
- **Strength** is something that gives you energy when you do it. It makes you feel _stronger_.
They're not always the same thing. For example, I've always been good with SQL. I could slice and dice data, write queries in my sleep. But being good at it didn't mean I *liked* doing it. And before I knew it, I was the "SQL guy," stuck writing endless reports instead of doing the work I was actually hired for.
That's the trap.
You become known for your talents. But if those talents don't line up with your strengths, your work drains you instead of fueling you.
So, what's this got to do with AI?
Well, think of AI—particularly the kinds of models we're using today—as a fast path to *average*. They're not perfect, but they're very good at generating a solid, median output, especially in areas you don't want to be spending your time anyway.
That's where it starts to get useful.
Take product management, for example. A big part of the job is being *present*—really listening, being in the moment, catching the nuance in what someone says. But for years I'd sit in meetings, frantically trying to take notes while also processing what people were saying. Someone would drop a gem of insight and I'd be scribbling so fast to capture it, I'd miss the next one.
Now? I use AI transcription tools. They let me stay fully engaged in the conversation, knowing I can go back and catch the details later. It sounds simple, but it's been a massive unlock. I'm not splitting my focus between listening and documenting—I'm just listening.
Or take another one: relationship building and community stuff on LinkedIn. Important? Definitely. High leverage? Sometimes. But is it where I do my best work? Not really. And yet I found I was spending a surprising chunk of my day there—writing responses, following up, doing low-level admin. So I started automating it with AI. Not the whole thing, obviously, but enough to get back time for the stuff I *actually* want to be doing.
That's the power here. AI helps you outsource the stuff that drains you, so you can double down on the things that fuel you. It's not about laziness. It's about *intentionality*—designing your workday to match your strengths.
But, and it's a big but, there are places where AI doesn't belong.
Sales outreach is one of those. We tried using AI to help with cold emails. You know what we found? They sucked. People already get enough beige, templated, obviously-automated garbage in their inbox. Cutting through that noise requires *craft*. You need to be thoughtful, targeted, personal. That's not a job for "good enough." That's a job for being *great*.
---
## Sometimes It's About the Journey, Not the Destination
There's another important angle here: *just because something isn't a strength doesn't mean you should always hand it off to a machine.*
Some tasks—especially the ones you'd classify as procedural or repetitive—carry hidden value in the *doing* of them. That value isn't always obvious until later.
There have been times when I've forced myself to do something that didn't feel high-leverage in the moment—digging through data, writing out reports, reading someone else's reports, manually tracking something over a few weeks. None of it particularly fun, none of it the thing I want to be known for. But in hindsight? That work gave me context. It let me spot a pattern I wouldn't have noticed if I'd just skimmed the AI summary. It sharpened my instinct for where problems were brewing. It made later strategic decisions better.
So yes, outsource the grind—but don't lose the feel for the craft.
Sometimes slogging through something inefficiently is how you build intuition. Sometimes the *act* of doing the work is what connects the dots. Sometimes you gotta "do the work".
The trick is knowing when you're learning versus just laboring. When the task is helping you build judgment, you want to stay close to it. When it's just draining your energy, that's your cue to delegate or automate.
---
## Embracing our left feet
So here's the simple rule I've landed on:
- Use AI to close gaps where "good enough" is actually *good enough*.
- Use it to offload talents that aren't strengths.
- *Don't* use it to automate the things that are supposed to be your superpower.
- And every now and then, choose the long road—because the long road teaches you things the shortcut can't.
This isn't about being 5% more productive. It's not about squeezing in one extra task before lunch.
It's about flipping the entire equation of how we work. It's about carving out the crap so you can spend the biggest chunk of your day in your zone. Swinging with your left foot. Leaning into the things you love. Being Messi.
AI isn't just a shortcut. Done right, it's a **reallocation tool**—one that lets you outsource your mediocrity and insource your brilliance.
And that's where the real magic happens.
## On entitlement
I'm so incredibly fortunate. My parents left their homeland to start a new life in Australia, an amazing country to grow up in. From the series of seemingly isolated events that led them to make that decision, right up to the ones that led to me living in San Francisco, it's difficult to imagine a probable alternate reality where things could have turned out much better. Everyone has complaints, things they wish were different, but if I had a choice to switch my current circumstance with some other random person on this planet there's no way I'd take it. Statistically speaking it's an almost guaranteed losing bet. I've lived a privileged life, and I try to remind myself how lucky I am regularly.
Few things irritate me more than when those that have it so incredibly good don't acknowledge it, don't appreciate it, or worst of all just straight up expect it. The depressing thing is that I see if far too often in the tech industry, it's one of the few ugly parts of working in the industry I do. As if we're entitled to everything and that the world owes us a free lunch.
## On free lunches
I get free lunches! Every day! And they're amazing! They seemed like a ridiculous excess that only made sense in The Valley until I moved over here. Now I can't imagine running a company without them. It's not because you should spoil your staff, but I'd just never appreciated how much productive conversation is had when you share lunch with colleagues most days. But it's a very fine balance. And I've been incredibly fortunate to have landed at a company that gets it, that doesn't spoil employees just because it can, but does it with due consideration. Makes sure that we're appreciative of what we have, that we're more productive and happier for having it, that we recognize the contribution each individual throughout the company makes in moving us closer towards our larger ambitions.
But it would be easy to get it wrong, easy for people to start taking it for granted. And what I've come to realize is that getting it wrong is the default path. You need to actively rail against complacency, show your appreciation, and every day remind yourself just how lucky you are. Speaking of which, [@HerokuVibe](https://twitter.com/herokuvibe), you're all amazing.
## It's so pervasive
It's surprising how easily this sense of entitlement gets hold of you. The proverbial straw that broke the camel's back, that got me to finally write up my thoughts on this, was Sarah Lacy's [complaints about Yammer downtime](http://pandodaily.com/2012/12/12/dear-yammer-and-the-entire-cloud-wave-if-you-expect-companies-to-use-your-software-it-has-to-work/). In particular I loved this quote, _"It was almost as if we were getting the value of what we’re paying. You know, nothing."_. It's incredible. Somebody, running a for-profit business, is complaining that their "core productivity tool" is down. Something that by their own admission is critical to the effective functioning of their business. And not only are they paying absolutely zero for it, they've now had the gall to take to the stage and publicly criticize said company for being so inconsiderate to have 4 hours of downtime. How dare they!
It's a slippery slope, I think I've been there before. GitHub has become a part of my work flow for a number of years now. And there's been times when they've had an outage and I've been tempted to join the Twitter rage at the injustice of it all. But you know how much it _really_ impacts me? Zilch. I get up and go grab a long overdue walk and a coffee. I come back and it's probably working again. Worst case, I might have to actually talk to someone or work out how to use my iPhone as a phone. It's such a minor inconvenience with such minor impact in the grand scheme of things it seems ridiculous to complain about it.
## Pay it forward
So I pay GitHub. Real cash money, out of my own account, for features I don't even use (all my repos are public these days). Why? Because I get value out of the product. Because it's important to my productivity. Because I want this company to be around in a few years and continue to make my life better. I even paid to join App.net, and used it twice. I like Twitter, I find it moderately useful, I want it to be around in a few years. But they wont take my money, so I'm giving it to someone who will in the hope they wont try and turn me into the product. I've bought all the software on my computer, I stream my music via my Spotify subscription. I still pay for Flickr, I hardly use it, but I want it to be better.
I see this all the time with users of technology these days. The expectation of a free lunch, the disconnect between the value people get from a product/service and what we're willing to pay for it, the belief that our only obligation is the minimum that is demanded of us. Followed by the indignation when we're asked to pay what it's worth, the outrage when something we depend on ceases to exist, and the denial that we're at all complicit in it's demise. But we are complicit. It's like we somehow forget that on the other side of that transaction, that product, that service, that lunch, is someone who is probably just like us trying to do their job and get rewarded for doing so. That for some perverse reason this other person should be thankful for the fact we're taking up their time, and should do so without complaint or without the expectation of anything in return. But that's not how functioning societies work, commerce is built on trust and the implication that an act of generosity show by one will be reciprocated.
Entitlement isn't just ugly, it's dysfunctional. And over the long-term unsustainable.
## Keep it real
A friend introduced me to [this clip from Louis CK](http://www.youtube.com/watch?v=mfmmNif5WCw) earlier this year. It's served as a great reality check whenever I feel a great injustice in my world.
## On empathy
Making things is fun. There’s a special sense of accomplishment that comes from creating something from a series of parts that previously seemed to be nothing. There’s a joy that comes when that something is deemed useful or enjoyable by another person. And a gut-wrenching feeling of depression when others write off your work as useless because it doesn’t solve their problem.
## Hell is other people’s code
We’ve probably all experienced it at least once. A chunk gnarly piece of code you’ve inherited from developers that have long since left the project, that makes no sense, and possibly doesn’t even do what is required. What on earth were they thinking? How could any person the calls themselves a professional write such atrocities for a living?
I’ve heard people get “care mad” about this before. Blindly raging at a name in a commit history, only because they care so much. And it’s right to care. To take pride in what you do. To appreciate the craft.
## It’s compromises all the way down
I write most of my code in ruby these days because I value the expressiveness and the readability, and I’m willing to compromise runtime performance to get it. I want to live within walking distance of where I work, I had to compromise on my initial budget for rent to make that happen. Everything we do is a compromise of some sort, especially so in software. We’re always having to make a decision between optimizing for performance, quality, speed of delivery, readability, or one of any number of other factors. Often we don’t stop long enough to explicitly acknowledge the compromises we’re making. Intuition and experience can guide us on auto-pilot, especially when a specific priority is clearly in focus (a.k.a a deadline).
## The right solution for the problem
Empathy is a tricky emotion to master, it rarely comes easy to me and I have to make a concerted effort to exercise it whenever I can. Dealing with someone else’s code is always an interesting scenario. Lacking any context the right assumption is always that they wrote the perfect implementation, given the constraints and priorities at the time. Maybe there’s a more efficient way, but performance wasn’t a priority and a deadline was. And maybe they didn’t have time to learn a better way, whatever worked was sufficient. I don’t have to agree with their priorities, that isn’t want empathy is about. But I need to be able to understand them.
And so when I hear engineers claiming they’re “care mad” about how bad someone else’s code is, what I’m really hearing is that they can’t understand another person’s needs. Which is a shame because empathy is a core engineering value. Without it you’re destined to write software that doesn’t solve people’s problems.
## On meritocracy
Over 10 years ago I worked at company that prided itself on the fact that it was a [meritocracy](http://en.wikipedia.org/wiki/Meritocracy), and even at the time it seemed at best aspirational and at worst cringeworthy. Everyone knew that it wasn't always true, and even companies with the best of intentions know it's not completely true. Since that time I've travelled, lived in different countries, and worked for a large number of companies. At some point along the way I've come to appreciate that the reason why meritocracy was a lie was much more troublesome than I'd originally realised.
## The lack of objectivity
In many organizations there is an unfortunate pattern where performance is rewarded via a promotion in the corporate hierarchy. We've likely all seen the negative side-effects of that: people promoted into management that have no desire to be good managers but crave the recognition, or feeling overlooked and undervalued on a project given the amount of effort we put in. When the credit goes to someone who played a lesser role it always stings and speaking up against it seems petty. The thing is there's no objective way to measure the contribution of an individual within a team, but we try any way. The person who reaps the most reward is usually the one who talks the most to the right people. And if you're busy delivering the project chances are you're too busy to be telling everyone how busy you are being busy. Kate Matsudaira gave a [great keynote at FutureStack](http://futurestack.io/videos#kate-matsudaira) earlier this year which touched on many of the same topics but in the context of how to be a better leader and manager.
We can't isolate our own biases. Are we rewarding this person because they did a great job? Because they put an amazing amount of effort into the work? I mean they even ended up being late for a really important football match that their daughter was playing in, so their personal sacrifices need to be considered. Would you have even known that if you weren't such close personal friends and had lunch on the weekend? Do you know what every other employee was meant to be doing on a random Thursday night?
It's a such a web of complexity. So many subtle variables. So many things to measure, so many things you can't. It's not to say you shouldn't try, you just can't ever say you've got the whole picture. It's a selective and largely subjective measure of contribution and any action you take to give a person what they deserve, what they merit, is skewed by that sampling bias.
But that's only the most trivial of the reasons why this approach is broken.
## The lack of historical context
In my experience our advancement in life continues as we get older. Each subsequent advance building on the previous one as we continue striving to move onward and upward. Our current position of advancement at any point in time plays a large part on how old we are and how long we've been doing whatever we've been doing. And our measure of what we deserve, our merit, is the accumulation of all that advancement and experience. It might follow a linear path like this:

So our merit can be measured simply by measuring the area under the line at any point in time. Easy! If only it were. You might intuitively sense that this steady linear progression isn't grounded in reality. So lets adjust it based on my own naive perception of my own experience:

There are long established cultural and societal expectations on an individual based on nothing more than their age. Arbitrary numbers which suddenly mean you're no longer an adolescent, you can be trusted to vote, you're allowed to drink. Literally overnight society has deemed you deserve more, because you lived another day and completed another lap of the sun. But there's also significant things I've undertaken that have contributed to where I am today. I first started selling my own software when I was 12, I switched to .Net when it came out in 2002, I picked up ruby around the time Rails came out. These all proved to be pretty significant events for me:

How on earth was I able to start selling software at 12? I was privileged enough to go to a good school, which in turn was fortunate enough to have a computer in every class room (a big thing at that time). But I was also fortunate enough to have parents that ran a shop selling PCs, and I got my first computer when I was only 2. Because my father was an electrician and was experimenting with them himself pretty early on. Being a qualified electrician meant that the Australian government paid most of his relocation costs to move to Melbourne in the 1970's because of a shortage of skilled labour at the time. And he'd been able to become an electrician because he had a large family that placed a high value on a good education, and we're able to help put a roof over his head while he studied. There's a whole series of events, coincidence, good timing, or just dumb luck that happened prior to any deliberate focussed effort on my part or before I was even born that have contributed in a significant way to where I am today. What if my dad had never become an electrician? What if he'd never moved to Australia? What if my parents weren't able to afford a computer for me at such a young age? What if he'd never opened a PC store?
And then there's the ugly truths about this privilege that are much harder to admit. What are the chances the Australian government would have paid for my dad's immigration in the 70's if he wasn't Irish? Would my mother have received the same standard of obstetric care if she was an indigenous Australian? What would my own health in my childhood been like if that were the case? What kind of schools would I have gone to if my parents weren't both White Catholics? Did my dad joining the Freemasons mean that we had a more supportive network than other new immigrants to a country?
All of these things fed into who I am and what I've achieved in varying amounts. So my chart actually looks more like this:

And here we can visualize one of the problems with meritocracy. Those with good intentions think they're only evaluating someone based on their most recent work:

But it overlooks that even a single achievement is built upon a foundation of previous achievements, and a lot of privilege. You think you're rewarding one small thing, but a different thing played a much larger role in the outcome. I might commit most of my waking hours to exercising and improving my craft and over time that too plays a significant role in my advancement. But being a straight white male has had 32 years of compounding interest applied to it:

I hope that on a day-to-day basis it plays an increasingly small part in how or why I advance, but it's undeniable that it's a pretty major reason for why I am where I am today. Maybe even collectively the largest reason.
# So what's the alternative?
I can't change who I am, but I can at least be cognizant of who I am. And I can be mindful of the words I use when talking about my own self-worth and the worth of others. It really shouldn't be a surprise privileged groups of people talking about "what they deserve" (i.e., meritocracy) causes other groups to get incredibly offended. If you want to overlook the multi-generational privileges bestowed upon you by the constructs of our society that's fine, but you deserve to be corrected.
Besides, you wouldn't give someone a medal at the Olympics if they had a head-start.
## On hiring
Articles talking about the importance of finding the right co-founder for a company are a dime a dozen. Finding a co-founder is the very first hiring decision a business has to make, it may actually have happened before anybody realises a business exists. And there seems to be a general consensus that the founding team can ultimately make or break a new business, so it's an incredibly important decision.
Something people don't seem to state so categorically though is: When does hiring become less important? Well, it really never changes. You may have less need for that visionary figure but a bad hire can be incredibly damaging to a business of any size. I've worked at a number of places over the past 15 years both as an employee and as a freelancer/contractor. I've seen many different ways companies go about the process of growing and it feels like where I am at Heroku has the closest to a consolidation of best practices I've come across yet.
## Restoring the balance
There's usually an unnatural power dynamic that occurs in the job interview process. Most commonly it's that the employer has most of the power: there is one job, multiple candidates, they get to pick one. Occasionally the balance of power swings the other way, people with a particular skill are in short supply and demand is high. Both of these situations lead to people reacting to short-term pressures and that's never aligned with the longer term success of the company or the individual.
Something Adam Wiggins stressed to me early on was to treat the whole process more like it was dating than a job interview. It needs to be a mutual appraisal, both parties trying to gauge the suitability of each other and whether this is going to work as a long-term relationship. Not just "could this person do the job?" but "is this the kind of company and team I want to work with for the next 2 years? Will they help me be who I want to be?".
## Respect
An integral part of levelling the power imbalance is treating each other with mutual respect. It can seem obvious, but so much of the regular interview processes is disrespectful of a candidate's time. It's tempting to get someone to come in for an interview, it feels formal and official and like you're being serious. Except as an interviewer it's costing you 30–60mins of your time, meanwhile it's costing the candidate that time plus the extra time they allowed to give a good impression by being early plus the transit time to get them to your office from wherever they're meant to be instead. In all likelihood their time commitment to that one interview was probably 2X what the interviewer had to commit.
It starts with screening, a lot of screening. I operate on a number of assumptions when someone sends in an application for a job:
- They've spent 5–10mins reading the job spec
- They've spent at least 10mins learning what Heroku does
- They've spent at least 10mins making minor tweaks to the CV or cover letter to make it relevant to the advertised job
Those are not all always true, but I'll give everyone the benefit of the doubt. Most have probably spent countless hours actually using Heroku. So the very least I do with every application is invest 30mins reviewing it and seeing what I can learn, it's the very least the candidate has committed so far. Reading an application usually only takes a few minutes so most of the time will actually be spent trawling the internet. GitHub, LinkedIn, Twitter, personal blogs, conferences they've attended/spoken at, finding out what kind of things their previous employers do, etc.
For those that get short-listed it's then a phone call, always. Even if the candidate is in the building next door, it's a call. It's time-boxed to a hard 30mins and I reinforce upfront the dating type nature of the process, I want to be getting asked questions too. What do they care about? Why are the changing jobs? What do they need to know about me and/or Heroku to understand if we're the best place to help them achieve that? The 30min limit helps keep the conversation focused and makes us both prioritize what we want to talk about.
And the call will happen at the agreed time, no later. Nothing is more important than hiring the right people and making sure we don't hire the wrong people. Writing code can wait, that other meeting that is about to run over can resume later. And I'm not about to send a message to someone potentially so important to my own success that I don't value their time.
We'll do at least 3 of those phone calls with different people within the company before we get someone into the office. It's amazing how much insight you can gain into their previous experience, technical aptitude, and ambitions from a few really focussed conversations. The total time commitment from the candidate at this point is somewhere around 1.5 hours, so the same as if they'd come into the office for a single interview. Yet we have a far deeper understanding of each other and what we're looking for.
## Make it real
After all the Googling and all the phone calls, we're usually left with only a couple of people where it still feels right and so next comes a starter project. To extend the (already strained?) dating analogy, this would be introducing our date to our friends or family. It's getting serious, we think it's good, but we're not proposing just yet.
This is where the time investment for everyone really ramps up. It's 3 days working full-time with the team, on a real project, exactly as we ordinarily would. So we have a quick planning session, look at our backlog, agree on something we could ship within 3 days, and then make it happen. It's the ultimate test to whether the candidate has the skills to do what we need, if can we work together effectively as a team, and if Heroku can provide the environment to help the candidate prosper and achieve their own goals. And sometimes the answer is no. We've had people work with us where technically everything clicked but the candidate decided it just wasn't quite the right fit. That is both an unfortunate and amazing outcome. Far better to have learnt that after 3 days where we can all walk away friends than in 6 months. Employers all too often forget that changing jobs is a pretty big and potentially life-changing event. For all of the excitement of a new job, maybe a new city, and maybe a pay rise comes the uncertainty associated with "is this going to work out?", a new expensive lease, having to build a new social group, the credit and health insurance implications of changing employers. When things go well it's great, but it's not completely risk free. Investing as much time as possible upfront to reduce that risk for everyone pays off in the long run.
The staff turnover rate at Heroku is the lowest of any company I've ever worked at. People have left, but not many. People have been fired, but not many. I've always had ethical problems with companies that take a hire quick/fire quick approach, it feels lazy and disrespectful to the employee. It treats people as though they are fungible assets and usually means nobody is stopping to address the failings in your hiring process that lead to you hiring the wrong person in the first place. People deserve to be treated better than that.
## Hiring is hard
I just did some rough calculations based on the last role I hired for at Heroku. Given the high number of applicants, all the screening calls, a few starter projects it came out to a collective investment from me and my team of 110 hours over 4 months to hire 1 person. Almost 3 weeks of full-time person effort. But this is a long game and I want someone who is going to help me, my team, and Heroku be even better in 2 years.
So why wouldn't you have someone spend 3 weeks focussed on the most important thing for success?
## Origin stories
It's 1939 and war has broken out in Europe for the 2nd time in just over 20 years.
A young man born in London, now living in the US, accepts a new job to try and help
the Allies fighting in Europe. This horrible, bloody war has ushered in a range of
sophisticated technology on both sides. In particular it's introduced the widespread
use of aircraft as a means of attack. Bombing raids are standard strategy; cripple
the opposition by destroying their manufacturing capacity. It's the modern
interpretation of Medieval siege tactics. Cut off supply, weaken them, starve them
into submission or an into inability to defend themselves. Only this time the collateral
damage is much higher.
The young man is William. William's team in Manhattan was working on a way to win the war. It wasn't the now well
known "Manhattan Project", though this was an ultimately even more expensive endeavor,
it was a way to see beyond the horizon. Through the clouds. To see the new airborne
threat long before it arrived, and give the Allies a chance to both defend themselves from
the oncoming attack and mitigate any losses. In six short years they turned what was little
more than theory into one of the most significant technological advances the world had seen,
and changed the outcome of the war in the process.
After the war William focussed his efforts on a new problem: a commercially viable alternative
to vacuum tube amplifiers. They were the switches that enabled the first computers. Looking
something like a cousin of an incandescent light bulb and being housed within a thin glass
shell meant they were particularly fragile. Which was a problem when a computer required more
than 17,000 of them. The advancement of this new computing technology would necessitate a more
durable, or alternative, solution. Over the next 10 years William and his colleagues
(there doesn't seem to be enough camaraderie to call them teammates) would invent, and
continue to refine, the transistor. However in 1956 William would leave his job on
the East Coast and move west to be with his ageing mother. A much more temperate and
comfortable place to spend your final years than the often harsh extremes of New York and
Jersey. Setting up his own company in a nearby suburb. His attempts to attract
former colleagues to join him failed, so instead he assembled an enthusiastic young team
of local scientists and engineers, focussed on expanding on and commercialising his
previous research by using new more efficient materials.
## The traitors!
Everyone has had a bad manager. William was one of the worst.
Paranoid. Combative. Quick to anger. His new company was barely a year old when a
group of employees went above William's head, to the owner of the parent company that
had funded their efforts, and demanded he fire William. When that failed they left to
start their own rival company. William was hurt. He told them they were traitors, and
that they'd never be successful.
## The legacy
William was right about a lot of things. That silicon would be a much more suitable
material for transistors than germanium. That those eight individuals would become
known as traitors.
They were many things. Unsuccessful they were not.
Robert Noyce. Gordon Moore. Eugene Kleiner. Victor Grinich. Julius Blank. Jean Hoerni.
Jay Last. They're what is now known as the Traitorous Eight.
Together they founded Fairchild Semiconductor. Then some left to found Intel.
Others AMD. Eugene teamed up with another partner to start the VC firm Kleiner Perkins
Caufield & Byers. Others still went off to build technology that enabled the Japanese
digital watch revolution by companies such as Seiko in the 70's & 80's. There's a
traceable lineage of direct involvement of William's early team in companies beyond
Intel, AMD, and KPCB and into Amazon, Netscape, Symantec, Intuit, Macromedia,
Sun Microsystems, and many more. As many as 65 of the companies that have come to
define our progression into a digital age.
And that doesn't even consider the way in which every company on the planet has been
effected by that early discovery. How every single act of invention and progress in our
entire history as a species has been improved, if not completely reinvented, to
take advantage of those silicon transistors. That it has completely changed the way
we eat. The way we travel. The way we learn. The way we heal. And the way we communicate
with each other.
The very law that has predicted the pace of this innovation was discovered by one
of this very eight, and it holds his name. Over 50 years later it still holds true.
## Do it again
It feels like every major city is trying to replicate the success of Silicon Valley
these days. Desperate to extract that special essence that makes it so unique. That
allows it to now, and forever, hold a special place in history in terms of its impact on
the world. And in its continued impact for over half a century.
There's the huge of investments by governments. Federal. Local. Would-be founders
bemoan the lack of access to capital, so there's tax incentives to try and make that
happen. Partnerships with foreign cities to try and encourage more trade and transfer
between locations. Corporate innovation programs to inspire "intrapreneurs" to
reinvigorate tired business models and product strategies.
What if it was _never_ about the capital though?
What if all it ever needed was temperate weather, a handful of talented and motivated
people, and one _really_ bad manager? William Bradford Shockley Jr.
## Portable, Shareable Application Development Environments
## The emphemeralization of my possessions
I remember a time when managing, securing, and protecting all my possessions seemed laborious. There was a pile of cassettes, CDs (and later DVDs) that had to be boxed up and moved whenever I moved house. The music had long been ripped to CD, but it still spanned multiple external drives and I needed to keep the CDs incase something ever went wrong with one of the drives. Then there were the photos: some 8x9 prints that had been scanned, and an ever growing directory of digital only ones. That again needed to back backed up onto external drives. And of course there was meant to be an offsite copy of those, incase there was a house fire. But whatever offsite copy I had was rarely up to date.
Little by little that’s changed. iTunes and an iPod replaced much of the physical clutter I had for music, later the iPod was replaced by a phone and Spotify. As for the photos the relationship flipped, my “offsite” copy was where I kept the whole library (S3 to begin with) and only a small working set was ever on my computer at a time. Even the hassle of keeping copies of software lying around on CDs with serial numbers disappeared thanks to [some scripts to install all my free open source software](http://github.com/glenngillen/bootstrap) and the Mac App Store.
It wasn’t until a couple of years ago, while talking to a friend about the risk of crime and property theft in San Francisco, that I appreciated the magnitude of what had happened. 10 years ago someone breaking into my house didn’t just carry a huge emotional impact, but it significant upfront and on-going financial ones too. TVs, home theatre systems, computers. Thousands of dollars of equipment and potentially months of effort to acquire replacements and set everything up again. Not to mention the sentimental things like photos that could never be replaced. But today the financial impact is $999 to replace a Macbook Air and 30mins-60mins to set it up. All the “irreplaceable” music, photos, and software is back exactly as it was in the time it takes to eat lunch.
Taking something that seemed to have a high negative impact, and making it near negligible has been liberating. And so I’m constantly looking at how to take it further.
## Outsourcing your development environment
I’ve been playing with [Nitrous.io](http://nitrous.io/) on and off for about a year now and I really love the approach they’re taking. As quick as it is to setup my local machine the biggest and most variable time sink is getting it to a fully working dev environment. Installing XCode to compile binaries has historically been a pain, and making sure any C-based dependencies compile when jumping to a new OS hasn’t always been reliable. And I never actually ship code OSX based so doing all my development on a Mac OS is breaking one of the tenets of the 13-factor app methodology: [dev/prod parity](http://12factor.net/dev-prod-parity).
Wouldn’t it be great to not have to install GBs of software just so you can write code? Wouldn’t it be great to be able to instantly access your familiar dev environment, from any computer, within minutes? And wouldn’t it be amazing to be able to share that environment with a colleague so you could work on a problem together, even if you weren’t in the same room?
Some of you might be agreeing but thinking that none of this is new and people have been doing it for years. And you’d be right. What [Nitrous.io](http://nitrous.io/) is doing differently is that they’re running it for me. It’s the difference between me paying to have a server in a rack to store all my photos, and then monitoring it’s uptime and managing backups, and working out how to fail over or just storing them all on Dropbox or S3. Running my own instance to host my dev environment is just moving the problem, I want to remove it.
## It’s not all rainbows
There’s been a couple of things that have held me back from going all-in in [Nitrous.io](http://nitrous.io/) though:
- Having to keep my vim scripts synced: It’s a minor thing, and it’s largely scripted, but it just feels like a hassle. If/when I’ve got all my development happening there it’s probably not an issue, but right now I’ve got a local environment configured to my liking that I want to be able to use. They had a Mac App to help address that, but it doesn’t work with Mavericks :(
- Collaborating via the web IDE: Partly an extension of my previous issue, I like the setup I’ve got and I want to use it. Editing in a browser still doesn’t feel right to me, pairing can be challenging enough without productivity impacts.
- Shared vim/emacs config: religious wars have started over less. At previous places I’ve worked we’ve converged on a single configuration so everyone’s computer felt familiar while pairing. Given part of my goal here is to share a remote service, everyone will be on their own computers, so converging on a config seems like it’s a needless constraint.
Once I stated the problems, it was easier to reason about a solution. All I needed was:
- The ability to edit files like they were on my local machine.
- To make sure file changes were synced to [Nitrous.io](http://nitrous.io/), and that other users would receive those changes.
Part of the experience of pairing is watching live what your pair is doing. That can happen via screen sharing, but I wanted to see if I can remove the screen sharing and instead have my own terminal open and watch the changes live. That means there’s an explicit contract that both users need to uphold.
## Editing files like they’re local
Well I cheated. I’m actually editing local files, and then using [unison](http://www.cis.upenn.edu/~bcpierce/unison/) to push the changes to [Nitrous.io](http://nitrous.io/) over SSH. To make make sure whoever I’m pairing with can see my changes I need to make files are saved in near-realtime. Enter the [vim-auto-save](https://github.com/vim-scripts/vim-auto-save) plugin.
## Detecting remote changes
Unison is set to run repeatedly every 1 sec (I originally had fswatch setup to run it only when local files changed) so changes will be propagated in both directions. The other part of the contract each user needs to uphold is to ensure they detect and display any changes to files within their editor. For vim that required some hacks to my `.vimrc`.
## Packaging it up
To make it easy to move this setup onto a new machine, I’ve consolidated all of config changes into a single repo at [https://github.com/glenngillen/nitrious.io-pairing-setup](https://github.com/glenngillen/nitrious.io-pairing-setup).
## A tale of two investments
I have a _hypothetical_ proposition for you. Well two actually, but you need to decide which of these is
more appealing and better for you in the long-term.
## Option 1 - A leveraged convertible note
I've got a special arrangement through a friend who can get you into a somewhat limited
investment. It's a really hot company right now, _everyone_ is trying to get on
board.
It's usually a minimum $1M investment.
The arrangement we've got in place means we'll let you buy in with as little as
$200K. But you'll still have a holding at a $1M valuation! It's structured in a way
that means you'll need to pay the interest exposure on the
note until there is a liquidity event or until the term of the note, at which point it converts into
ownership. And there's operating costs and a management fee
that you'll need to fund. All these costs are variable but expect them to be in the range
of $75K/year-$80K/year.
The term of the note is 30 years. And similar companies have grown on average over 7%/year
over a 30 year period.
You can liquidate your holdings early and at any time, subject to market conditions. It'll
typically take anything from 90 to 180 days to find another party to take over your position
for you to be able to liquidate. And if you do it within the 30 year term there will be
additional contract termination costs to pay.
If all this sounds appealing there's a $42K non-refundable application fee to get it setup.
## Option 2 - Take a sabbatical
Take 4 years off. Well, not _"off"_ necessarily. But you'll receive an average wage for 4 years.
The only condition is that you're not allowed to be employed by anybody.
Go on an extended holiday. Go back to school. Spend time with your family. Start a business.
Do whatever you want. Just don't take a job somewhere.
# So what do you do?
Put years of considerable savings into the thing everyone else is? Or spend 4 years searching
for something with higher returns?
What would it take to create something that offers more than a 7% annualised return per year? 10%?
Y-Combinator expects their companies to drive that kind of product growth _per week_. Yes I know
that's not directly tied to revenue at that early stage, but over the long-term it should be a rough proxy.
4 years gives you the luxury to try a bunch of things to see what worked.
Surely you could find _something_ in that time that would provide
a nice additional income stream. Worst case at the end of those 4 years you go back to work, right?
And you at least got to spend a bunch of time with friends and family.
# The fallacy of investment in home ownership
If you've not already realised Option 1 isn't actually a convertible note for a company, it's buying a house. Specifically
it's the proposition anybody thinking about buying in Sydney is currently weighing up.
Except nobody talks about it in these terms.
We've been sold a collective delusion that buying your home
is an "investment", that "owning your home is The Great Australian Dream", and that "rent money
is dead money". So most just march blindly forward along with societal expectations on
how they should manage their money without pausing to question what they're actually doing.
Let's break down those numbers:
- $1,025,478 is the [median house price in Sydney](http://www.domain.com.au/news/median-house-price-falls-in-capital-cities-for-first-time-in-three-years-real-estate-institute-of-australia-20160311-gng4ug/)
- I've assumed the buyer can front up the $205,095 to meet standard lending criteria and avoid additional fees for mortgage insurance, etc.
- $42,221 are the [estimated costs](http://www.smh.com.au/money/tools-and-guides/calculators/stamp-duty) associated with purchasing a property of that value in Sydney.
- The Reserve Bank of Australia estimates the [ongoing running costs of owning a home are at least 2.6% per year](http://www.sbs.com.au/news/article/2015/11/05/comment-rent-or-buy-lets-do-sums).
- The [NAB mortgage calculator](https://www.nab.com.au/personal/loans/home-loans/home-loan-calculators/loan-repayments-calculator##nab-loan-calc-results-repayment) estimates monthly repayments of $4,399, principal & interest at a 4.99% variable rate.
The numbers in other Australian capital cities will be lower but the general shape of everything will be the same.
Let me debunk some of the myths and preconceptions around home ownership, so we can
get back to a rational place to compare the two options.
## It's not treated like an actual investment
Seriously.
_Nobody_ treats it like an actual investment. If buyers did then they'd think about
what they're doing more critically. Does that Option 1 sound compelling enough to justify the exuberance
we see around home ownership? If people were taking out a $1M dollar loan for absolutely anything
else they'd question the numbers a lot more. They'd be more cautious. They'd assess the true opportunity
costs associated, look at what the alternative options are. At the very least they conclude it's actually a more efficient
deployment of capital to continue renting + buy an investment property than it is to own your home.
If the government seriously considered it an investment they'd treat it as such from a tax perspective.
You'd be able to claim the ownership, interest, and depreciation costs as deductions. Like in the US. And
we wouldn't even be debating whether "negative gearing" was a blight on the economy because we'd just
take it as understood that houses, all houses, were a bona-fide asset class.
But that isn't happening.
Because we don't treat it like an investment. Because most of the things
we prioritise for in a "home" are deeply personal and detached from a sound valuation. Does it _feel_
like a home? Could I imagine raising my family here? Do _I_ like the shops nearby? Is it close to that
one particular school I want my children to go to? Does it have the space I need for all of the material
possessions I've accumulated over the years and can't bear to part with?
All perfectly valid reasons to buy a home. Just don't fool yourself into thinking they're investment criteria.
And don't get me started on the moral dilemmas of having the primary vehicle of wealth creation for individuals
built upon the requirement of making it more unaffordable for people to put a roof over their head.
At best owning your own home is a massive inflation hedge. Gone are the days of someone owning and living
in one house their entire life. You start with something just barely affordable. Your salary goes up. Equity
in the property goes up. The family grows in size.
And then you sell it, to buy something bigger. In a nicer area.
The equity you gained is just parlayed into a deposit + costs on a bigger house, with a bigger mortgage. You're not net ahead
because even though you struck it lucky timing wise and got a few years of 15% growth, \*the rest of the market increased
by 15% too\*. The same pattern repeats ad-infinitum until you decide to downsize and reign in your lifestyle.
Hardly the dream of wealth and independence we've been sold.
## But the leverage!
> Where else can you get 5X investment leverage? Or 10X if you're willing to pay mortgage insurance?!
> --- L. Iterally Everyone
Well to start, my margin account with my stock broker covers at least half of my capital requirements on
any trade. So even the most exotic of trades I'll get 2X leverage without any effort. If I'm trading FOREX
margin requirement for currencies like USD is 2.5%, so 40X. Bonds go as low as 1%, so 100X. Same is true
of some US Mutual Funds.
Don't feel like a trading on margin? I don't blame you. How about some warrants? Citi have some available
that let you acquire an interest in an [Exchange Traded Fund](http://www.asx.com.au/asx/markets/warrantPrices.do?by=underlyingAsxCode&underlyingCode=VTS).
Those bastions of consistent long-term gains.
It's closer to 3X leverage, minus some interest costs. But there should be no on-going costs.
The dividends make the interest payments and pay off your loan to buy the rest of your share in the
fund (if it falls short you'll have a final payment to make in 2020). And you'll possibly be able
to claim the interest costs as a deduction.
All those numbers are based on a total of 15mins searching on 2 websites.
So that's to say there's plenty of other options with comparable or better leverage for anybody
who takes the time to look. All of them more liquid. None of them with the exorbitant operating costs.
## Rent money is dead money
Imagine there's a new development with a house you'd just love to live in. Actually there's two identical
houses, side by side, for the exact same price. Do you buy and move in? Or buy, rent yours out as an investment,
and pay rent to live in the identical one next door?
For the sake of clarity and consistency we'll use the same median Sydney house prices. The upfront costs are
approximately the same whether you've moving in or renting out so lets just call that a wash for simplicity.
It's what happens next that matters.
### Moving in
- You've got those repayments of $4,400/mo.
- You've got ownership & operating costs the RBA estimates to be $2,600/mo.
- You no longer have to pay any rent
### Renting yours, moving in next door
- You'll probably take an interest-only loan on an investment property, so your repayments are only $3,400.
- Your ownership & operating costs are still $2,600/mo.
- You have to pay rent, probably around $3,000/mo.
- But you also receive rent for your property, $3,000/mo.
So the rent is a wash. The operating costs are the same. And the mortgage repayment is $1K/mo less as
an investment... because we took an interest-only loan. Why was that?
Because as an investment we can claim all the costs, including the interest repayments. And so there's
less of an incentive to be paying off the principal. That $400/mo shortfall between the rent received is
a deduction on your tax return. As is the $2,600/mo in operating costs (well, most of it). So that's $3K/mo,
$36K/year in tax deductions on your annual return. If you happen to be on the highest marginal tax rate
you're over $16K/year better off renting.
And you're still living in the exact same house!
I need to point out that if/when you sell the properties the way the gains are treated are vastly different,
with the home you owned _and_ lived in letting you pocket the gains tax free. But see my previous point regarding how
this is just parlayed into larger homes and bigger debt. Those gains _need_ to be tax free to allow that hamster
wheel to spin. And I maintain you're not actually ahead as a result.
All this is to try and illustrate what most people think they know about home ownership is horribly wrong.
With our expectations just slightly adjusted lets revisit what it takes to start a company.
# Starting a company is lower risk than you think
The cost of starting a software/SaaS type company these days is virtually zero, all you need is a credit
card and time. Most IaaS and PaaS providers have incredibly generous free and low-cost plans that
are production capable. I know from experience that the free plans from a previous employer
are more than capable of running production services of a reasonable size.
You'll no doubt want more money at some point. To hire more people. To pay professionals to do a better
job on various things (like design). But lets assume now your primary limiting factor for getting a new
idea to market is actually time.
So how do we get you more time? Let's re-assess that Option 2.
## Keeping the money
The [median earnings for someone living in NSW](http://www.abs.gov.au/ausstats/abs@.nsf/Latestproducts/6302.0Main%20Features5Nov%202015?opendocument&tabname=Summary&prodno=6302.0&issue=Nov%202015&num=&view=)
is $1,527/week, or $1,162/week after tax. That's just a
shave over $60K/year after tax. For anybody who's in a position meet the standard lending criteria for
for that median Sydney property they'd need to have saved at least $200K. Closer to $250K to cover stamp
duty and legals.
This is the basis for Option 2. Don't buy the house. Keep the money. Do something with the time you
just bought for yourself.
## Think of the leverage!
As far as property investment goes, you've actually got relatively few levers. I mean you can build and
add more living areas, but the value of that depreciates the longer you hold the property. You could
give the bathrooms and kitchen a make-over, but they depreciate too.
Hrm. :confused:
Actually everything you can do to the property devalues over time. Property investing 101: land appreciates, buildings depreciate.
The entirety of your long-term gains will come from decreasing availability of land alongside increased demand from people.
Things that are also entirely out of your control.
Maybe I'm old-fashioned. I prefer investments where I can manage my risks. Where I can take action to
increase my returns. Where there's a probability of me doing more than simply getting the average market return.
You know, novel things. Like:
- Increasing prices to increase margin.
- Lowering prices to increase the number of customers.
- Reducing my unit costs.
- Providing good service to reduce churn.
- Building new features to acquire new customers and/or new pricing options.
- Content marketing to acquire new customers.
- Paid marketing to acquire new customers.
- Implementing an SEO strategy to acquire new customers
- _etc._
The problem is usually not having a lever, it's knowing which one has the most leverage.
## The ultimate in flexibility
How serious are you about your idea? Want to increase your focus by removing the immediate distractions?
Want to increase your runway?
Take a working holiday.
Pack your bags and your laptop and head overseas. There's thousands of beautiful places to lose yourself
while you build an MVP. Almost all of them cheaper to live in than Sydney, most of them with faster internet :wink:.
# Still not convinced?
When the market inevitably turns one of these options means you can double down your efforts
of building something great to keep afloat, while escaping to glorious beaches in South East Asia to reduce
your overheads and maximising your returns.
It won't be those with a $1,000,000 mortgage for the next 30 years.
## Agile Development: The quickstart guide to doing it right
Lots of places don't do agile right, in fact most do it wrong. There are a number of factors why, chief among them I believe is that the percentage of people who have worked somewhere that nailed it completely right is so low that there aren't enough to go around and lead by example. So here are a few of the tips I've been given or learnt over the years, credit has to be given to [all the wonderful places I've worked](http://www.linkedin.com/in/glenngillen) over the years. The ones that have done it well, and the ones that have done it poorly… you can learn just as much from reflecting on where it all went wrong.
## Back to basics
Are you already using agile, or are you planning on implementing it in your company? If you're reading this chances are something isn't working as well as you'd like and so it's time to reset. First thing to do is throw out your preconceptions on agile, ignore almost everything you've read in practitioner manuals, because at it's core it's really quite simple. Most problems stem from people picking only certain features of agile or trying to let a tool guide the process for them.
### Refocus on the manifesto
It's worth going back and looking at the [agile manifesto](http://agilemanifesto.org/) and then questioning each aspect of your process to ensure it is giving priority to the values on the left side. If it's not, remove it.
### Throw out the tools you're using
Scrumworks? Urgh! VersionOne? Atlassian? They're all cut from a similar cloth. If you're not already doing agile correctly, they will just get in the way. So until you're up and running, get them as far away as possible.
For planning, all you need are some index cards and a pen. If that doesn't offer you enough protection for your DRP or wont work because you've got a distributed team then use a wiki. Again, avoid at all costs anything more complicated until you've got it all working.
> I mention later in this post that much of this insight on how to do agile properly came from working with [Graham Ashton](http://effectif.com/). He's since written an [agile planning tool](https://www.agileplannerapp.com/?utm_campaign=quickstart-agile&utm_source=glenngillen.com).
> Unsurprisingly it avoids all the problems I mention above. If you want a web-based tool, check it out.
## Planning
The great thing about stripping it back to basics is that it re-aligns focus on the things that are important, you're not distracted about calculating backlog points and predicting velocity. Instead you are collaborating with your customer to understand what they will consider working software.
### You need to become the customer
I mentioned in my last post, [nobody cares what tools you use](/thoughts/nobody-cares-what-tools-you-use), your customer is trusting you to make the right decisions to do the best job. You can only really do that when you understand the requirements completely, a full and deep appreciation for _why_ the work is being done and not simply _what_ needs to be implemented.
Your customer is unlikely to be a software developer so their scope, as broad as it is will be, is limited by their experience. They also can't possibly know upfront all the questions you want answered and all the seemingly irrelevant detail that would feed in to giving the best user experience possible. Expecting them to bridge this gap is completely unrealistic, instead the gap needs to be bridged by having the developers move towards the customer. The developer needs to be completely bought in to the product, understand the problems it solving, and how they'd best be solved.
This is precisely the reason why [tools like cucumber don't work](/thoughts/you-dont-win-friends-with-salad), they are bridging that gap from the wrong direction. And if we go back to the agile manifesto, that approach carries the potential to skew the priority back towards contract negotiation rather that customer collaboration.
### Customers don't write the stories
And neither do business analysts, development managers, or project managers. I'd go so far to say that if you want to be "agile" and you've got a team of BAs then you should fire them all. Anybody getting between the developer(s) and the customer is stopping the developer from becoming the customer. Crucial detail will be missed and misplaced assumptions will go unchallenged. Sure, you'll still get a product out the door but it will either be what was agreed (which is often different to what is actually wanted) or it will not be as awesome as it could have been.
The people that get between developer and customer have the best of intentions, they're trying to let developers focus on writing code. I'll say it again though, they're job isn't to write code it's to deliver a solution. It's a false economy to think you're saving the developer time by taking on the requirements analysis on their behalf, it will take longer for them to appreciate 3rd hand the requirements than the half day it would take to know them first hand _and_ write them up themselves.
Pull up your sleeves, get out a set of index cards, some pens, and some A4 paper. No laptops, no ipads, no technology. Force the customer to explain the issue without computer aids as though you know nothing. Each of you draw up wireframes, compare the differences between them to see what assumptions have been made, write the card together. Make it a tactile experience.
The result should be a concise description or the business problem, and the end-user benefit you're trying to deliver. Never more than a couple of sentences (maybe a few footnotes for pertinent implementation details that can't be neglected).
### The most important stories get done first
This part of it will be difficult, if not impossible, for the developer to make a judgement call on. It is often very difficult even for the customer, but it has to be done. That being said, it shouldn't be as difficult as many people make it. If you're working on a new app that's not yet been released I've got a diagram to help you evaluate priorities:

It's also worth keeping a focus on getting the smallest usable feature set out the door as soon as possible. Eric Ries said a good way to get to that point is to outline the minimum features you think you need before you can release it publicly, and then you can probably reduce it by 90%. You always need much, much, much less than you ever expect.
If it's an existing app and you're having problems prioritising it might be worth speaking to some users. Just make sure you keep focus on doing the smallest and simplest thing possible to meet the requirement.
A good way to gauge if story is really _the_ next most important thing to get completed is to double the estimate, if that changes the priority in the customer's eyes it wasn't as important as they thought.
### If you're not doing the work, you don't get to estimate it
It took me a long time to appreciate this as a manager, I was often guilty of sizing up tasks based on how long I thought it would take me to complete it. Then someone else picks it up and I wonder why it took so long. Truth is it would always take me longer too but that is too easily forgotten, do you really have a load factor of 1? I thought not.
How easy or difficult a task is has so many variables: familiarity with the code base, how recently you've solved a similar problem, access to an existing solution, etc. and each of those come from a fairly personal experience. Sit down, with a pair to rationalise the decision if you like, and come to a conclusion you're happy with. The important thing is that you are consistently optimistic/pessimistic on your approach, it's less important to get the estimate right than it is to be wrong on a relatively consistent basis. Everything averages out very quickly when it comes to the planning.
### Don't mention time
Do everything you can to avoid estimating in hours or days. No matter how much you explain things like load factor when you see a story with "2 days" or "8 hours" written next to it people can't help but think that is how long the task will take. So you can't be entirely shocked when 3 days in they ask how that task that was meant to take 2 days is going. Avoid the awkward situations completely by not setting the expectation in the first place.
Estimate in points, or jelly beans, or anything other than hours or something that can be tracked by a watch or calendar.
### Estimates aren't guesses, but they're not accurate either
Don't take a look at a story, close your eyes, and come up with a number. Take the time to analyse in detail what is required, what systems you'll need to talk to, read the 3rd party API/integration docs. Spend a whole day or more if you have to and write up a detailed list of tasks in the order you expect to tackle them, including any decisions on implementation you may come to, and put them at the bottom of your story card.
Time invested here pays for itself two-fold later in the iteration by maintaining focus on the minimum set of tasks that need to be completed and setting your direction each step of the way. Plus it gives you confidence that the task is achievable and unlikely to blowout.
When it comes to putting a number (points, jelly bean, whatever) on an estimate I now always advocate a 3 point system. 1 point roughly equates to a half day of effort (ssshhh, don't tell the customer ;), 2 points a whole day, 3 points 2 days. The reason being that it's too difficult to consistently estimate small tasks, if you estimate an hour for something and you get snagged and it takes you the first half of a day (not unusual) you've blown the estimate by 400%. At the other end of the spectrum you've got two problems, either a story that isn't really implementing the most simple solution possible or a broad scope that it's difficult to really sit down and estimate in the detail required to be confident in it.
If the tasks are obviously much smaller than 1 point, I'll try and group a couple of similar smaller tasks into a single 1 point task. If a task is bigger than 3 points, I'll split it into separate deliverable parts. "But it's really a 5 point task, everything has to be delivered at once!", Bollocks!
### Estimates for work that isn't scheduled are worthless
They might be useful for getting a high-level idea of when some story might be delivered, maybe, if it's ever actually scheduled. Remember one of the core tenements of agile is responding to change, and nothing impacts priorities like actually giving software to users. So it's not uncommon for that really-urgent-we've-gotta-have-it next feature to get bumped for a glaring hole a bunch of users pointed out. And then bumped again for something else. It comes back to expectation management again, and putting an estimate next to it adds a misplaced finality to where the story sits in the priority queue.
The other issue is that each iteration of development feeds back into next. Some previously completed work may make a later story easier to implement, or it may actually highlight that something is more difficult than originally expected. Either way there are enough factors that can completely invalidate whatever estimates you come up with.
## Execution
Once the planning is done, it is down to actually starting the work. It's not always a matter of ticking boxes off the work list though, the most effective teams I've worked in have shared some common traits.
### Start mid-week
Few people love a Monday morning and getting motivated can sometimes be an issue. If you compound that difficulty by expecting everyone to be in the right mindset to sit down and write stories, well it doesn't always work out for the best. There is no real long-term cost associated with moving the start of the iterations from a Monday to a Wednesday, but allowing me to be productive first thing after a hazy weekend by allowing me to look at a list of work and just crack on with it is a huge win.
### Have a fixed iteration length
I prefer to work with 2 week iterations, 1 week feels too short and 4 too long.
There is a comfort that comes from having some order to the world, and the importance of the general psyche and morale of a team is often underestimated. On those times that a story does take longer than expected, it's nice to know that you've still got a week or so to make it up. It's also great to have a regular sense of completion in predictable intervals, the dreaded risk of being stuck on the same task that is going to take months never materialises (partly because you never schedule a story with more than 2 days of effort, right?). There is a lot of subtlety in the impact this predictability has on developers which makes it hard to pinpoint all the benefits, but when combined they all make for a happier environment.
From a management perspective, it's great to have some clarity on when things are going to be delivered. And I'm not just talking about for the next fortnight, but you know on a specific day each and every fortnight something is going to be delivered. So does the rest of the team. It makes planning much easier, people know the most convenient windows to take holidays, and which days to stay home are particularly inconvenient because you're planning with the customer.
### Developer attention is a premium
Jason Fried from 37signals has a great video explaining [why you can't work at work](http://bigthink.com/ideas/18522) that I'd recommend watching. When you're deep in the coding zone nothing is more disrupting to your flow than someone coming and interrupting you to ask you for opinion on something completely unrelated. That 10 minute interruption can take 30 minutes to recover from fully to get you back into the same state. If they happen 2-3 times a day that is up to 25% of the workday productivity lost to distractions.
And as Jason mentions in the video the very action is basically a way of saying "Hey, my immediate needs are more important than whatever you're focussing on, give me attention". It's selfish and unhealthy for productivity. Remove phones. Email people if you have to, but don't expect a response that day. Inter-team or important but non-urgent questions should be posed via an instant messenger, chat program, or something that doesn't demand immediate interruption (like phones and meetings do). What about things that are urgent? Well that needs to be assessed on a case-by-case basis, but the truth is truly urgent things happen very very rarely. Almost everything can wait at least 4 hours.
It's here that a good development manager can work wonderfully, acting as bouncer and preventing direct access to developers while they're busy coding and filtering the urgency of all requests.
### Pair Programming
A fairly contentious part of agile is pair programming. When it works well, it's hugely efficient and carries lots of additional benefits (higher quality code, quicker delivery, lower documentation requirements, less "single developer" business risk, etc.) but when it's implemented poorly it's just a waste of a resource. In most places I've worked it sadly falls into the latter camp, and it's because people are just paying lip-service to the practice and working it as mentor/tutor type role rather than a pair actively writing code together.
That's not to say it doesn't also offer great potential as an approach to training, the important thing is both participants need to be as involved and active in writing the code.
#### Owning the workspace
The biggest problem when it doesn't work is normally down to the setup of the workspace. Generally Person B brings their chair over to share Person A's desk, and Person A stays almost exactly where they were to begin with. It's guaranteed to fail. Here's what you need to do:
- Share a single monitor, you both need to be looking at the same screen. They're big enough these days that you can see everything you need with appropriate window management.
- Each person gets their own keyboard and mouse, no co-piloting on shared inputs and it's not fine to have one person with a keyboard and the other person using the laptop it's connected to.
- Divide the screen in half, then mark that virtual line on the desk with some tape or a marker… right down the desk. Each person gets their side and never should they encroach on the other half.
It might all seem a bit contrived and naff, but it makes a difference to the efficiency of the operation. Body position plays on the sub-conscious and determines whether you feel comfortable using the keyboard in front of you. Sitting too far to the side and you start to feel that you're a spectator on somebody else's computer, too far in front and you feel like you're a pilot and co-pilot rather than peers.
#### Everyone is in their own context
You're going to know what programs you need open to get the job done. For me it's an IDE, a terminal or 3, and a web browser or the running application. Agree on a common screen layout where everything can be seen at the same time, and then mandate it across the entire team (I've found [Divvy](http://www.mizage.com/divvy/) really useful to keep it consistent). It means that when you come to someone else's computer it doesn't feel foreign, it's just like being on yours but at a different desk.
Most importantly though is that even though you're both working on the same task together on the same machine, you're probably both in slightly different head spaces and thinking about a slightly different aspect of the problem. Quickly switching between applications to satisfy your own curiosities can have a terrible impact on whatever it was your pair was thinking about. Being able to see everything at the one time prevents you from unexpectedly switching context on each other.
Make sure you get a monitor large enough to make it work. Given you're going to spend ~8 hours each and every day staring at it, it's not sensible to buy a cheap screen.
#### Silence is golden
This is difficult to nail until you've developed a good working relationship with your pair, but the best communication between the two of you is the non-verbal. For the same reasons above, you can never be sure what the head space of your pair is and interrupting them to mention a typo could completely break their train of thought. Instead wait for an obvious pause or context switch and simply point it out. Eventually it may get to the point where you know each other well enough that even more subtle queues like a shift in posture give away that you need to re-think the code you just wrote. But at least you can then do it once you've got your current thoughts committed to screen.
#### 80 characters is enough for anyone
Like many of the tips on here, they've either been passed on to me by [Graham Ashton](http://effectif.com/) or refined further by working with him for 3 years. This one seemed particularly pedantic to me, but I went along with it because it really wasn't that difficult to adhere to and he was adamant that he'd accept nothing less. And it's this, no line of code should ever extend beyond 80 characters.
I now try to enforce it wherever I go.
It took me a long time to fully appreciate how important it is, at least a year, but code readability is drastically improved and that is always a good thing. When pairing though, it is mandatory. You don't have the luxury of scrolling indefinitely to the right to read the full line of code to see the intention because doing so will interrupt the flow of your pair. Worse still, it actually takes the bulk of the code off the screen.
It also prevents a nasty scenario where something important that drastically changes the intention of a line of code is cleanly hidden by the window edge and the real flow of the application is the opposite of what you thought was happening. Say something like the following:
def my_example_method
raise "Here is an example of something you would think gets raised!!!" unless coder_reads_this_part?
end
Refactor any line that stretches over 80 characters. It only requires a little thought, makes the intention clearer, ensures you can see all the important code at once, and stops you from having to break someone else's focus.
### Test Driven Development
Tests first, then the code. It's not just about ensuring you've got stable and working code, it's about keep focussed on doing the smallest thing possible to make the tests pass.
This approach works great with pair programming, and here's the formula: One person writes a test, the other makes it pass. Once it's passing the person that just wrote the code writes the next test and hands it over. While one of you is trying to write a crafty test to keep the other busy for a while, your pair has already devised a cheeky way to have it always return the value you want and has devised a new test to catch you out.
It's generally a quick back and forth iterative process where you're each trying to outfox the other with edge cases or overly simplistic implementations that pass because the tests are robust enough. It's almost game-like, and eventually you both come to a stalemate. At that point you sit back and realise the story is complete, and with excellent test coverage.
### Don't break the build
A continuous integration server is a must as is some form of reporting of any errors raised in production, and problems on both must be treated with a suitably high priority. Anything that is deemed "flakey" and raises an alert for non-legitimate reasons needs to be fixed immediately. As soon as the team starts to doubt the notifications, legitimate problems start to slip through too. It's a bit like the broken windows theory, if you allow these things to go by without action then it breeds complacency and soon some failing tests become accepted as the norm.
That all means that before any developer commits any code back upstream, they run all the tests… all the time. They are there for a reason, and it's inexcusable to not be running them. The CI box is there as a backstop only.
### Deliver at the beginning of the end
If you're starting your iterations every second Wednesday then code needs to be completed at the close of play the Monday immediately prior. That means you've got a full day to deploy the code to production and catch any unexpected errors. The other benefit of the mid-week approach is that on the rare occasions it all goes pear-shaped people are more inclined to stay back a little late and fix it. Trying to keep people back after hours on a Friday isn't just difficult, they've often already mentally checked out and off on their weekend and you run the risk of actually making a bad situation worse.
If it all goes seamlessly and you roll-out and have a day free, fantastic! Time to look at that ever growing list of bugs your users have been sending back that haven't been getting scheduled into an iteration. You can always find small tasks to fill the day that are incredibly useful but not super urgent.
Avoid bringing planning and unscheduled work forward, you'll skew your workload for the next iteration and run the risk of over-stressing developers and/or over committing. The benefit of _maybe_ squeezing in a day of development isn't worth the additional risks.
## Review
The code is released, people are using it, and everyone is happy. Now it's time to look back and see how things went, what went really well and what could be improved upon.
### Run a retrospective
Before the planning session, get all the team in the room to talk about the good and the bad of the previous iteration. Do it quickly, you should need an absolute max of 30mins but you can probably do it in much less. Get index cards or post-it notes of two different colours, one for "went well" and one for "needs improvement", and everyone has to write at least one thing on a card of each colour. There are no limits to how many things people can list though, allow it as a cathartic forum for all issues to be brought out in the open.
Put all the cards up on a board (grouping similar topics), so everyone can see the balance of opinion on how things went. Ensure everyone understands what all of the cards mean and what they are referring to. Now everyone gets 3 points to spend on the "needs improvement" cards, they can spread the points out across 3 cards or put them all onto a single card (or the obvious option in between). The 3 cards that have the most points the team agrees to make a concerted effort to improve upon during the next iteration.
It's only after a couple of iterations with consistently re-appearing top rated problem that I'd consider going back at looking at some of the tools I mentioned throwing out at the top of this article. And even then, only if you're certain they're the best way to fix the most pressing issue facing your team. If I'm honest, I don't think it's going to come up.
### Split partially-completed stories
Occasionally a story only gets partly done, and there is much gnashing of teeth when people have to work out what to do with it. Because you've been taking an iterative test driven development you've got a bunch of code that is written, tested, and passing even though the story is incomplete. Fantastic!
Look at what is left to complete and write up a suitable story to reflect it, then go back and revise the previous card to reflect what was actually done. Now look at the original estimate and try and work out what percentage of the story you've completed, apply the appropriate number of points to each story (keeping in mind that if you're following along with my 3 point/jelly bean system, 3 points = 2 days. So if you've done half you've got 1 day on each or 2 points on each).
That way you get an accurate reflection of both what was completed in the previous iteration and a fair idea of what remains, based on your original estimates (remember, we wanted continual optimism. No revising estimates retrospectively).
### Calculate your velocity
Now you know how much work you got completed, you can with a fair degree of confidence predict how much you can do next iteration. After a few iterations, you can look back and calculate the average for the past two or 3 to flatten out any particular fast or slow ones and get a good feel for what is achievable.
### Under-promise, over-deliver
No kid likes waking up Christmas morning to find the present they asked Santa for to be nowhere under the tree. Likewise, no customer likes being told you're going to give them something in two weeks and for it to not materialise. So if you're uncertain, you're better off scheduling too little work and then pulling an extra story in later in the iteration than over committing. It's not just about managing customer expectations though, it's about limiting stress on developers by keeping the targets realistic.
## Conclusion
These are the things that have contributed to making it work at places I've enjoyed working at. The most important thing is to measure overall success of your process as it's ability to create a happy and productive environment, good things will naturally flow from it. At the very least you should apply the principles of agile to the implementation of the process itself. Nothing is set in stone, pause at regular intervals to review the success, adjust the most important aspects to give people what they want.
## Safely migrating a Ruby app from Heroku to AWS Serverless
> This is a multi-part series on how to safely refactor and migrate a ruby-based app from Heroku
> to running entirely serveless on AWS. Each step is incremental and self-contained, with special
> attention given to avoiding any need to wholesale lift-and-shift the application so as to avoid
> the risks such large changes introduce. This is the distillation of more than a year of lessons
> learned, but that's mostly a by-product of the complete lack of urgency on my part for completing
> this. This playbook could be followed as quickly as your own urgency and confidence desires.
I've a project that's been running happily on Heroku for many years now. It's mostly been a set and forget app,
but from time to time it requires some attention. It's also a permanent fixture on my credit card
statement every month much to my chagrin. It's only only a few hundred dollars which isn't by any
measure an enterprise level spend, but it's always felt disproportionate to the actual needs of the app.
It's very bursty in terms of the traffic it serves. When it gets traffic it might service several thousand
requests per hour. But between those times... nothing. Literally nothing. There's no baseline load. Yet
it's _always_ running 2x dynos, a couple more for async workers, a production database. This is the
exact type of app that is a perfect fit for a more serverless and scale on demand approach.
## Step 0 - Define a pragmatic plan
Now, this is _my_ plan. It worked well for me precisely because I wanted to re-architect the implementation anyway. It might not be the right plan for your app though. I hope in going through this that you get some inspiration rather than a blueprint to copy. There's aspects of this that could likely be adapted.
Overall what I hope you take away is that there's a path migrating things safely. In small steps. With no downtime. And you can have it happen as quickly or slowly as you like. My migration took many months! Not because it was hard,
but because there was no urgency. I reaped many of the cost and performance benefits early and so the last bits just dragged on becase... well, they could.
### Step 0.a - Create a Terraform Cloud account
Trust me, it's going to be easier to let HashiCorp take care of some of the moving parts here. Everything we
need is available on the free tier. Sign up for [Terraform Cloud](https://app.terraform.io/) and create a new organization. We'll use it later on.
### Step 0.b - Importing the existing Heroku config into Terraform
To make this transition easier, in addition to always having a reversible audit-trail of the changes
that have been applied at any point, I imported the existing Heroku app into Terraform so that I could
manage it via code. While meaning there is now a single approach to managing the infrastructure irrespective
of where it's running, it also made it more straightforward to connect the require contact points without
manually copy & pasting values into different systems.
It starts by setting up a new Terraform project and adding the Heroku provider:
```hcl
# terraform.tf
terraform {
cloud {
organization = "the-name-of-the-organization-you-created-on-terraform-cloud"
workspaces {
name = "the-name-of-your-app-or-project-goes-here"
}
}
}
```
```hcl
# providers.tf
terraform {
required_providers {
heroku = {
source = "heroku/heroku"
}
}
}
provider "heroku" {
}
```
Now that you've created the bare minimum required for this project, run `terraform init` to setup the project and
load the providers. Next it's time to create the basic Terraform config for your Heroku app, here's a pseudo-example
of mine (consult the official [Terraform Heroku Provider Docs](https://registry.terraform.io/providers/heroku/heroku/latest/docs/resources/app) for all of the available options):
```hcl
# heroku.tf
resource "heroku_app" "your-app-name" {
name = "your-app-name"
region = "us"
acm = true
stack = "heroku-18"
config_vars = {
}
}
```
Now you can sync the current state of your app with the Terraform config you created by importing it:
```bash
$ terraform import heroku_app.your-app-name your-app-name
```
## Step 1 - Switching database to DynamoDB
With the knowledge that this will all eventually be a lambda-based workload, keeping postgres in the mix
was going to be a problem. It's also not going to solve the "paying for things that are always running
but rarely used" problem. On the other hand, [DynamoDB On-Demand](https://aws.amazon.com/blogs/aws/amazon-dynamodb-on-demand-no-capacity-planning-and-pay-per-request-pricing/) does exactly that.
This also happens to be one of the most impactful changes in terms of reducing costs from our perspective!
### Step 1.a - Creating a DynamoDB instance
If you're not already familiar with DynamoDB this is going to be a big conceptual change! I would very strongly recommend you get familiar with it first and realize how you'll have to rethink how you do almost everything! As you'll see though I end up running Postgres and DynamoDB side by side, even having data for a single table split across both stores at various points, without the app missing a beat. So it's not like you need to burn everything down and start again. You do need to know your data access patterns though.
I'd also strongly recommend that if you're new to DynamoDB you watch a bunch of [Rick Houlihan's videos on YouTube](https://www.youtube.com/results?search_query=rick+houlihan+dynamodb) to see how you should adapt your thinking coming from an RDBMS world.
Once you know how you're going to structure your data it's time to start creating the tables! My first one was one of the most critical, my `accounts` table. As I had fairly bursty workload I didn't want to have to pay for pre-provisioned capacity I wasn't going to use and so I opted for the pay-per-request pricing option (all up _everything_ we're going end up with here will cost me a few dollars per month, down from the multiple hundreds it cost on Heroku):
```hcl
resource "aws_dynamodb_table" "accounts" {
name = "accounts"
billing_mode = "PAY_PER_REQUEST"
hash_key = "id"
stream_enabled = true
stream_view_type = "NEW_IMAGE"
attribute {
name = "id"
type = "S"
}
attribute {
name = "email_address"
type = "S"
}
}
```
### Step 1.b - Granting permissions to DynamoDB from your Heroku app
And here's where the cross provider magic of Terraform came into shine. Defining a DynamoDB table in AWS is one thing, but I now somehow have to grant my _Heroku_ app access to it. I'll explain the specifics shortly, here's how I did it:
```hcl
resource "aws_iam_user" "heroku" {
name = "heroku-user"
}
resource "aws_iam_access_key" "heroku" {
user = aws_iam_user.heroku.name
}
resource "aws_iam_user_policy" "heroku-dynamo-access" {
name = "heroku-dynamo-access"
user = aws_iam_user.heroku.name
policy = < item_hash[key],
'action' => 'PUT'
}
end
self.class.dynamo_table.update_item(
key: { self.class.hash_key => self.send(self.class.hash_val) },
attribute_updates: item_hash)
rescue Aws::DynamoDB::Errors::ValidationException => ex
puts ex.message
puts "Error: Can't save: #{dynamo_params.inspect}"
raise
end
def dynamo_item
result = self.class.dynamo_table
.query(key_conditions:
{ id: {
attribute_value_list: [self.uuid],
comparison_operator: 'EQ' } })
result.items.first
end
def dynamo_item?
!!dynamo_item
end
def sync_dynamo
save_dynamo unless dynamo_item?
end
end
```
And the way I included it into my model:
```ruby
class Account < Sequel::Model
include Dynamo
dynamo_table 'accounts'
def dynamo_params
base = {
id: self.uuid,
... # More key/vals in here
}
end
def after_save
super
db.after_commit do
save_dynamo
end
end
```
So here's the general idea of what I've implemented:
- Update the model so that every save goes to DDB as well as Postgres. I do that by calling my `save_dynamo` method after the transaction is committed in Postgres. This also has the convenient property that it captures all new records as well as any updates to existing records. The moment this is deployed it freezes in time the complexity of any migration task. New data will be in DDB, and any actively updated data will be migrated just-in-time when it's updated.
- Each model implements a `dynamo_params` method that will create a hash of what to store in DDB.
- In the module: a centralised way to configure my access to DDB (using the config vars we set earlier) and for each model to specify the name of the table in DDB for that model (I avoided any inflection based naming magic and made specifying it explicit).
- Finally there's the `dynamo_item` method (to find an item by ID) and `dynamo_item?` helper to return if the a matching item exists in DDB, which are both only used by `sync_dynamo`. A function I could later use to migrate records one at time across to DDB. Or batch with something like `Account.all.each{|a| a.sync_dynamo }`
Within a few hours data is flowing into DDB. Come back tomorrow and I'll have a sizeable amount of things migrated.
The next step was to adjust the way data was being read. I already had helper methods for most of these which made it easier (rather than any dynamically generated methods generated by an ORM). And so I could update methods like:
```ruby
class Account < Sequel::Model
dataset_module do
def active
select(Sequel.lit('accounts.*')).where(deprovisioned_at: nil)
end
end
def self.find_by_email(email)
active.all.select{ |a| a.email_address == email }
end
end
```
To be something more like
```ruby
class Account < Sequel::Model
dataset_module do
def active
select(Sequel.lit('accounts.*')).where(deprovisioned_at: nil)
end
end
def self.find_by_email(email)
res = self.dynamo_table.query(
index_name: 'AccountsEmailActive',
key_conditions: {
'email_address' => { attribute_value_list: [email], comparison_operator: "EQ" },
'active' => { attribute_value_list: [1], comparison_operator: "EQ" }
}
)
if res.count == 0
active.all.select{ |a| a.email_address == email }
else
res.items.map do |item|
Account.find(item["id"])
end
end
end
end
```
There is a lot here to _not_ like. The degenerate case is kinda expensive performance-wise. It'll run a query on DDB, if the return count is zero it'll then run that query on Postgres and get the old results. If the first DDB query did look, it then makes _another_ query to get the full item (because not all of the data we need is availble in the GSI I created to search by emails).
It's one of those cases where it's important to understand your usage patterns. While there's definitely inefficiencies here the numbers involved, for me, mean that end users would barely notice. We're still single digit millisecond responses for _all_ of the database stuff. I was actually tempted to squeeze in a call to `sync_dynamo` for any row we pulled from postgres just to make sure that was the last time we'd ever retrieve that record from there.
### Step 1.d - Migrating the remaining data
I made my plans for just migrating the remaining data. With this in place (and a couple of other find methods implemented) the app was now primarily using DDB for all account data! The last step was to write a little migration script that was barely anything more than:
```ruby
Account.all.each do |account|
account.sync_dynamo
end
```
I think I may have maybe added some ordering on it, and chunked it out into a couple of threads, but that's the migration story. Just run through every record, call that method, if the record exists already in DDB it'll do nothing and if there's no record it'll save it. When that script completes there should be no more new lookups of data in postgres any more.
### Step 1.e - Stop writing to postgres
This is the biggest and riskiest change in the whole thing. We're querying as much as we can from DDB now, falling back to postgres where absolutely necessary. New data is still being stored in Postgres even though we're querying it from DDB so we can stop doing that. Doing that meant ditching Sequel though, and doing _that_ meant we also had to reimplement a couple of other features like validation.
To start, I replaced `Sequel::Model` with `Hashie:Mash` which gave me a hash-like structure (great given that's how DDB items are returned) but with attribute accessors like Sequel had (great because that's what the rest of the app is expecting):
```ruby
class Account < Hashie::Mash
include Dynamo
dynamo_table 'accounts'
def save
save_dynamo
end
end
```
Next was to make sure the way I created new records still worked. Again, I thankfully had helper methods already that made this straight-forward. Without them I'd probably explore some decoration of the `initialize` method on each model:
```ruby
def self.create_from_signup_form(signup_params)
new(signup_params)
end
```
That `save` method above needed a bit more help though. There's some functionality we got for free from Sequel or Postgres that we'd need to re-implement ourselves such as generating and ID and storing a `created_at` timestamp. Those would apply to all of the models though so they went into the module, while the `save` method was updated to make use of them:
```ruby
require 'securerandom'
class ValidationError < StandardError; end
module Dynamo
def generate_id
self.id ||= SecureRandom.uuid
end
def validates_presence(attrs)
attrs.each do |attr|
val = self.send(attr)
raise ValidationError.new(attr) if val.nil? || val.empty?
end
end
end
class Account < Hashie::Mash
include Dynamo
dynamo_table 'accounts'
def validate
before_validation
validates_presence [:id, :email]
end
def before_validation
generate_id
timestamps!
end
def save
validate
save_dynamo
true
end
end
```
There's some other custom validation logic and attribute setting I've removed for simplification but you hopefully get the general idea. That's it though! That's enough to remove Postgres for this specific model. Much, much, much testing later and I pulled the trigger and deployed the changes without any issue.
And there it was, the biggest and most important table in my app no longer using Postgres. I repeated the same exact steps one table at a time. The hardest one was done and so
the others were much quicker and easier to do. One at a time everything moved across until there was nothing lef using Postgres at all.
### Step 1.f - Decomissioning Postgres
🎶 _"And now, the end is here, and so we face, our final curtain..."_
You served me well Postgres, but it was time to say goodbye (for this app at least). I took a backup, downloaded it, and stored it in a couple of different locations just in case. Then I got the `DATABASE_URL` and saved that somewhere else incase I needed to roll it back. Because I'm extra paranoid I spun up a new empty database an attached it to my app. I then promoted it to be my primary (i.e., it now became the database referenced by `DATABASE_URL`) and restarted my app just to be sure. My app was now pointing at a completely empty database so if anything was going to break it'd be now. If it did, I could quickly switch back to the other DB that was still there.
But nothing broke. It all worked. And so it was finally time to run `heroku addons:remove heroku-postgresql` 😢.
## Next Steps
This is just the start! But it was a huge step both in terms of cost savings for my tiny app and also in terms of complexity of what had happened. I'd completely replatformed my database to a whole new technology without skipping a beat. Up next was:
- Step 2 - Replacing my workers with serverless functions
- Step 3 - Adding a CDN to provide flexibility in migrating the front-end & APIs
- Step 4 - Moving the API to API Gateway
- Step 5 - Serving the static content from S3
- Step 6 - Forcing everything else into a lambda container
Stay tuned for more details on how I approached each of these.
## Picks & Shovels
Capital in all it's forms, be it financial or just your attention, isn't evenly
distributed. Nor is the ratio of where it's allocated a constant. Stage theatre
may have a resurgence and the cinema box-office may wane. Reality-cooking shows
dominate the television ratings as the number of high-quality local dramas seems
to shrink. At some point the pendulum swings back the other way and things that
were old are new again.
Most people seem at least superficially aware of the phenomenon when it comes
to investment cycles: booms, bubbles, and bust. Property is hot, everyone wants
to buy a house. Mining was hot, now people aren't quite so sure. The art of the
game is to buy low and sell high, having the prescient ability to pick the breakout
winner and ride it to the top while cutting your losses on any of the other bets.
## Playing a different game
When there's a particular sector that's going through a boom period there's the
constant desire to throw your money in and ride the wave, hoping you pick one of
those breakout winners. In a property boom you can buy a house, and hope that your
house goes up more than the houses around it so that you do better than the average
return. Or you could invest in the companies that support that boom, that collect
their revenue from the winners. And more importantly the ones that don't win. The
entire sector is their customers, and so there's not the same dependence on picking
the winner. For a property boom like in Australia we're talking companies like CSR
and Boral. Or if you want more direct exposure to actual buildings you've always got
actual builders like Mirvac, Sunland, and Stockland. In most of those companies you'd
have almost doubled your money over the past 5 years, without the servicing costs of
having a mortgage.
That approach of investing is called a [pick and shovel play](http://www.investopedia.com/terms/p/pick-and-shovel-play.asp).
As best I can tell the term owes its origins to the Californian Gold Rush, and a very
shrewd Samuel Brannan that secured all of the pickaxes and shovels at the start of the
gold rush and then on-sold them at huge margins because of the monopoly he secured. The
term embodies much more than a single extortionate businessman though. It's Levi Strauss.
It's Amadeo Giannini. It's Domenico Ghiradelli. It's Henry Wells, and William G. Fargo.
Household names in California, if not the world, more than 160 years later. People who
built businesses to service an influx of demand, and then turned that into long-term
sustainable businesses with global impact that have stood the test of time.
## The tech axes
As we live through this once in a generation shift where everything from booking a hotel,
hiring a taxi, getting dinner delivered, or finding someone to walk a dog transitions to a
wholly digitally delivered experience, investors can try to mine gold by guessing which
consumer facing app is going to win. Or they can invest in the tools that make them happen.
My own seed investments have been banking on the latter, the cross-cutting problems that
service all product builders and benefit from the rising tide.
## Why I invested in StackShare.io
I've been incredibly fortunate over the years to work with some talented individuals while trying to solve interesting problems. Most recently I spent three and a half years working at Heroku trying to carve out a little piece of history by changing the way developers deploy modern web applications. Before I left Heroku (and as a result the USA) I spent a lot of time meeting with various VCs to get their perspective on the developer/software development space and what lay ahead of us. One made a comment in passing that stuck with me, "Heroku lowered the barriers in going from an idea to production. And they're now so low that having just an idea isn't enough. The days of thinking you'll be funded with just an idea are ancient history". I chalked it up as a minor win for all of our efforts.
## Apps are more than their code
In some circles Heroku are still only known for making deploying Ruby on Rails apps easy. `git push heroku master` was a revelation at the time and an approach that has been often copied since. But anybody who has deployed a non-trivial app to the platform knows the magic goes much deeper than that. You get a fully-managed postgres instance provisioned for you, with the configuration automatically inserted into your app's runtime environment. If you need redis there's a number of fully-managed options to choose from. Want to play with Elasticsearch? Again there's a number of managed services to choose from. And again the configuration is automatically inserted into your environment. Even more incredibly it takes only a second or two for all this to happen.
Never before has such access to technology been available to so many so quickly.
## Welcoming a new audience
Adam Wiggins, co-founder of Heroku, would occasionally speak of a future where programming literacy was treated with a similar level of importance as reading literacy is today. The early origins of Heroku as a Ruby on Rails focussed IDE you could use in your web browser was in part an attempt to move us closer to that reality, by placing the tools required into the hands of millions via a web browser. And he's not alone in that thinking, Marc Andreesen famously proclaimed 3 years ago that "[software is eating the world](http://online.wsj.com/news/articles/SB10001424053111903480904576512250915629460)" and as software disrupts more and more sectors it introduces wave and wave of new potential software developers to the potential of their new tools and systems. One of Andreesens partners at a16z, Sam Gerstenzand, earlier this year wrote about [the major potential opened up with this massive shift](http://a16z.com/2014/07/30/the-happy-demise-of-the-10x-engineer/):
> We begin optimizing success for problem solvers and business builders, not those who happen to be both these things and have been programming since age 12. We unleash new categories of startups, from builders with new backgrounds and perspectives. We unleash thousands of new startups that can now be built because this friction is gone.
## Some assembly required
I love Sam's comparison of software development to building with Lego blocks. There are many people who love the freedom of creation possible with a random assortment of blocks and can quickly assemble whatever it is they imagine. However Lego long ago recognised that not everyone is able to fully realize the potential of their little blocks without some guidance. For a few decades now the primary means by which kids around the world consume Lego is by purchasing a pack with instructions on how to build the object(s) on the box.
The same is increasingly becoming true with software and services. There's so much we can learn but seeing what people have built previously. What blocks did they use? Why those ones? What blocks should _I_ use if I'm trying to build a new thing?
## Seizing missed opportunities
There were a few things that I had hoped I could focus on at Heroku regarding Add-Ons, that I never got the chance to. One of them was discoverability and decisions. With so many different pieces of functionality that you could add to your app, one common challenge I heard about from customers was the need to help people make better decisions. Developers were on their own when it came time to picking Add-Ons, and that's still very much true today. And yet when weighing this problem against the myriad of others we had to deal with for the core platform, it was always clear that Heroku's focus had to remain on the hosting platform's DX.
Heroku, like many other providers is focused on providing the best Legos which is the best thing to for it to focus on. Without that core piece to build upon, everything else falls apart. But outside of the context of that core piece, the people using the Legos have 5-10, 15, 20 moving pieces, and they need help choosing the right pieces and putting them together.
On the opposite end of the experience, in my role at Heroku, I dealt with enterprise and developer-focused vendors frequently. I saw how most of them benefited from the distribution that Heroku provided. Add-Ons would instantly get access to Heroku's large customer base via the Add-Ons Marketplace; some services even launched there. In return for this distribution, Heroku has [revenue-share agreements](https://devcenter.heroku.com/articles/becoming-a-provider) in place. So I always knew that whoever was able to nail the Legos problem for users would also be solving the distribution problem for vendors, and as a result would be able to capture a lot of the value in that chain, just as Heroku's Add-Ons did. It would be a huge business opportunity.
When I first learned of Leanstack.io a few months ago, I immediately recognized the problem it was solving. After speaking to Yonas and hearing his vision for what would become [StackShare](http://stackshare.io/), it was clear that he had thought about and learned much about this problem; and more importantly, that he was going about solving it in the right way. Like most developer services, I believe a company solely dedicated and completely focused on solving a specific problem will beat anyone else doing it as a supplementary/complimentary activity. I think [StackShare](http://stackshare.io/) has the potential to do something truly amazing and help developers in a way that's never been done before.
That's why I invested in [StackShare](http://stackshare.io/). I've also joined as an Advisor and am excited to tag along for the ride.
## Why I invested in Stamplay
If you've seen me speak at an event any time in the past 5 years, there's
a good chance I at least touched on the major shift that is underway on
what it means to be a developer or to build an application.
Maybe it was how the emergence of NodeJS was enabling a whole new
generation of application developers. Designers who'd taught themselves
enough Javascript to demo live interactions to their clients, could now
implement the "full-stack". And deliver a visually stunning and polished
experience in the process.
Or maybe it was how the changing economics of open-source development
was providing an opportunity for people to build more than libraries and
frameworks. That in addition to sharing a solution to a problem via
a repo there's entire businesses setup to provide those Solutions as
a Service. People don't just want to avoid writing their own code from
scratch, many would like to remove the on-going operational overhead too.
Perhaps it was tying the thread between these and other ideas together to
talk about how [application delivery is increasingly the act of assembling
existing pieces together](/thoughts/coders-2021).
## Building apps with Lego
The Lego metaphor isn't a particularly new one. We had a block as the
logo for the [Heroku Add-ons Marketplace](https://elements.heroku.com/)
since it's inception back in 2009. What has changed in that time is both
the size of the market and the quality of the blocks within it. The
marketplace launched with as few as 5 services. Most of them custom
wrappers written and managed by Heroku, leveraging existing APIs. Within
a few years that grew to the more than 150 that you see there today.
And that's just the tiny fraction of the total market that we felt was
a good fit for the Heroku platform and its customers. Take a look at
something with a broader mandate like the [AWS marketplace](https://aws.amazon.com/marketplace)
and you can see that the market here is already huge.
The first problem is [knowing what's out there and what you should use](/thoughts/stackshare).
After that, it's then how do you actually use it? And what about all
those things you _already_ use? With most products and services exposing
and API of some sort, what many people need is to orchestrate the
communication between those APIs.
## Enter Stamplay
I've heard descriptions of [Stamplay](https://stamplay.com/)
range from "It's like If This Then That on Steroids", "It's like a version of AWS Lambda that I'd want
to use" to "This is going to put me out of a job". And depending on your use case it could be any
of those things, all of them, or something else entirely. They all resonate with me in various
ways.
It lets you create your own custom snippets of code to run, which then accessible via an API end-point.
You can chain together a range of existing services you use, passing data in and out of each until
you've built up something useful. That whole chain of events again exposed via a URL. Put a thin
layer of HTML and Javascript in front and you've got yourself a fully-hosted and managed application
that is ready to go. Or you've got an API that handles a data-pipeline for you. Or set up a scheduled
task to run to [push that experience out via SMS](https://blog.stamplay.com/build-a-daily-news-by-sms-service-with-twilio-and-scheduled-flows/), email,
or into your Slack team.
The possibilities are almost limitless. Because your own code is turned into bite-sized blocks that
can be mixed and matched with the hundreds of existing blocks your business is already using.
The reason this gets me excited is because it combines so much of the engineering flexibility of a service
like AWS Lambda, with the supporting suite of services so many MBaaS products have promised in the past, and the
added flexibility to be as bare-bones API or rich client experience as you need. And the team have already got
a wealth of well documented examples to highlight some of the common use cases:
- [Automating back-end workflows](https://blog.stamplay.com/introducing-flows/)
- [Building hybrid mobile apps with Ionic](https://blog.stamplay.com/introducing-stamplay-support-for-the-ionic-framework-for-developing-mobile-apps/)
- [Building native mobile apps with React Native](https://blog.stamplay.com/building-a-store-locator-with-react-native/)
- [A pizza ordering bot using Kick](https://blog.stamplay.com/how-to-build-a-pizza-ordering-kik-bot-part-1/)
- [Automatically converting new sign ups to leads in Salesforce](https://blog.stamplay.com/stamplay-adds-the-salesforce-api-integration-to-its-platform/)
- [A landing page so people can add themselves to your Slack team](https://blog.stamplay.com/launch-your-community-with-a-fully-automated-slack-signup-page/)
All of this, and the future it's moving towards, is why I invested in Stamplay last year.
## Tall Poppies
A month ago Tom Brady and his New England Patriots lined up for Super Bowl 51. Meanwhile questions around Brady's legacy were swirling. Was he _really_ that good? Had he just been lucky? Or even worse, manipulative? I mean his record, and the legitimacy of some of the Patriot's previous titles, had been tarnished with some controversy (Deflategate and Spygate). Coming in to the largest spectacle America puts on every year there were no shortage of people eagerly ready for Brady to stumble. For the plucky Falcons to run over him. And for most of the ensuing 64 minutes of game time they did. But history shows Brady and the Patriots staged the largest comeback in Super Bowl history, brought the game into it's first ever overtime, and ultimately won. He cemented his place as the greatest quaterback of all time. The haters would have to wait for another day.
Bieber, Nickleback, and Lady Gaga. Depending on where your preferences sit on the spectrum of music tastes it's unlikely you like all three of them. In fact you might not like any of them. You may even be among the millions of people who so actively and passionately dislike them that the distaste of their work has become a meme unto itself. I'm sure it keeps them awake at night. With their collective album sales exceeding 100 million. With the hundreds of packed out stadium shows.
At the start of this month the media reported that passing of "it girl" Tara Palmer-Tomkinson. Amidst all the speculation of what had happened the various press outlets described her as a "tabloid darling", which was a generous interpretation. Anybody who has read the British press over the past 25 years will know that's code for someone who has been mercilessly hunted by the paparazzi so that every social faux pas, wardrobe malfunction, or illegal activity can be documented in high resolution and splashed across what used to be the front page of a glossy magazine. If you didn't know TPT you've no doubt seen the pattern. Kim Kardashian at the moment. Paris Hilton before that. The cycle of celebrity who is celebrity because they are are celebrity. Where there is both a symbiotic but toxic relationship with the media, that is trying to simultaneously hold them up while tearing them down.
Where am I going with all of these rambling anecodtes? Just one more to go, I promise.
I was at PauseFest a couple of weeks ago, an event full of creative and entrepreneurial people listening to various talks about design, creativity, tech, and business. Listening to a panel talking about failure and hearing first hands accounts from people who had risked, and lost, it all only to pick it up and start again and find new success elsewhere. There was an international guest on the panel. And so eventually, it came. The question. It always comes when there's an international guest in town. I don't blame the moderator, he did a great job running the session. And he was just following a well trodden path of the dozens before him over the last 6 months. A dozen or so tech, startup, and entrepreneurial events recently. Most of them with a foreign expert fielding questions on how to help the local startup scene. And so, eventually, it arrives:
> What do you think we should do to address the [tall poppy syndrome](https://en.wikipedia.org/wiki/Tall_poppy_syndrome) in Australia?
... or a variation on that theme.
The thing is, it's nonsense a claim. Especially the "in Australia" bit. Envy and resentment aren't uniquely Australian traits. They're an unfortunate part of the human condition. Anybody taking the time to read the linked Wikipedia article will see the references to related concepts in other cultures. All of the travelling guests immediately get it. It's not a new idea. They just didn't have a name for it. A recent episode of NPR's Hidden Brain even discussed evidence that the [traits exist among chimps and early human societies](http://www.npr.org/2017/01/17/509561647/coronations-coups-and-keeping-up-with-the-kardashians). This is something that cuts across industries, cultures, generations, and even species.
What does feel uniquely Australian is the regular complaining about the idea that you might, one day, be on the receiving end if you're successful. Like some kind of excuse. Why should you even bother if people are just going to cut you down? Fine. Then don't. Everyone else who was already going to do something will get on with the work of getting it done.
I don't think Brady gave a damn that I wanted him to lose.
## Self-reflection on diversity
I worked at a company that had a problem with diversity, or more correctly the lack of it. That in itself isn't surprising these days. While there were a vast number of groups that were very underrepresented across the organisation, the lack of gender diversity was a very obvious problem. I think at one point we might have had 10X more guys called Matt than we did women in engineering.
So when I had the opportunity to hire people on my team I was adamant I wasn't going to maintain the status quo. This time it would be different. I'd try my hardest to not add a second guy called Matt to my team.
And then the applications started flooding in. Guy, after guy, after guy. I guess everyone was right, it's a pipeline problem. The disparity of gender within tech roles means it shouldn't surprise me that the applicants for the role would be as diverse as those currently employed in said roles. Well, actually, zero women applied. It probably means they've all got jobs they love already and aren't looking to change. Right? I mean, what else could it be?
## Some time away
We placed someone in the role and I decided I'd take a few weeks leave before he started. I spent a lot of that time pondering the process we'd been through. How the candidates that applied had found us. What it was about the role that had them excited. Why they wanted to come and work with our company.
Not a single person I spoke to approached us completely green in terms of their knowledge of the company. Most were customers. Many knew one or more of my team mates from their open source contributions, public speaking, writing, or social media presence.
Were we just not speaking in the right places? Were we not getting in front of the right people? Where did we need to be to change the status quo?
## Being part of the problem
I did a bit of a manual audit of who _I_ followed on Twitter. Ascertaining as best as possible what gender the people I followed identified with. It was pretty confronting. I can't recall what the exact split was but I'd estimate at least 90% of the accounts I followed were men. Of the women, if you removed the ones I knew personally via school/friends/previous employers, then you'd probably be left with less than 10 total.
So as a start I unfollowed many of the male #thoughtleaders I was following. Most of them had been regurgitating the same shit for years now, and I was sure if they said something new or interesting someone in my network would retweet it. Then I took to what felt like hard work at the time, finding some new role models. Promiment women in tech. It was a very gradual process, the public advocates fighting for diversity were the most obviously discoverable ones. And over time observing who they interacted, following the people who seemed to have similar interests.
Within a few weeks I had at least 5 applications from incredibly talented women. People I wish I still had a job to offer, but we'd filled it and stopped advertising months ago now. That also meant their applications weren't a response to me shouting out into the internet that I was hiring, those messages were long ago buried in my timeline. The only material differnce between now and a month or three previous is who _I_ followed on Twitter. Not my audience. Not the events I was presenting at.
It was less about where I was speaking, and more about where I was listening.
## The least you can do
The Australian Federal Government recently announced [$3.4M in grants being award to projects that are helping to address the gender imbalance in tech](http://www.startupsmart.com.au/news-analysis/these-24-government-grant-winners-have-won-3-4-million-to-transform-the-stem-sector/). 24 projects in total, run by amazing and motivated people.
People who understand the problems in this industry, and who have ideas along with the passion
and determination to fix them. If you have even the slightest interest in helping address
problems with gender diversity in STEM the least you could do is take the time to listen to them.
Literally, it's the least you could do. Because I just spent the past two hours collating all
their Twitter details so that all you have to do is click the follow buttons below:
Follow @GirlGeekAcademyFollow @SarahMoranFollow @april_stainesFollow @lisykFollow @designjunkiesFollow @tammybutowFollow @WISparkvilleFollow @embwsFollow @AVERTtrialFollow @SBEAustraliaFollow @catrionawallaceFollow @TopazConwayFollow @klsinclairFollow @amandaprice1002Follow @https://twitter.com/atse_auFollow @shefliesauFollow @KEJoyce2Follow @oneplanetwomanFollow @Qld_Rural_WomenFollow @alisoni71Follow @trudy_azzopardiFollow @bronwyn_reidFollow @regionaldevaustFollow @cbr_inFollow @innovationsarahFollow @thewarrencentreFollow @ScienceAUFollow @witwaFollow @pia_turcinovFollow @consultingroomFollow @dmattheysFollow @tarynmusgraveFollow @dianaadornoFollow @anitrarobertsonFollow @educhangemakersFollow @EduSumFollow @maddyscottjonesFollow @RiAus
## Startup accelerators & incubators
Running a successful company is tricky business. It requires
a lot of determination, hard work, support, and a good amount of luck.
But you can make yourself luckier.
The right people. The right place. The right help. At the right time.
Find the perfect intersection of those attributes and your hard work
and determination might just take care of the rest.
## Learning from the best
There's thousands of programs setup to help entrepreneurs on this journey.
They cover the full spectrum in terms of both product stage and
program quality.
Most famous among the tech startup community is
[YCombinator](https://www.ycombinator.com/). Each intake bringing
in 100 or more hopeful teams for 12 weeks. An intense period of
incubation within the YC chrysalis. Hypotheses tested. Products validated.
And at the end we're all dazzled by the emergence of a ~~butterfly~~ unicorn.
It's a model many have tried to replicate, but none as successfully. In part
because success breeds success. The value of the YC program isn't just the
wisdom of those running it. It's the size, strength, and value of the alumni
network it has created. The years of churning out successful companies and failures.
The ability spot patterns based on similar cases in their relatively extensive
history.
Whatever your specific problem, they've almost certainly had a company with that specific
problem before.
It's no longer a matter of just distilling generalised "business advice" to
young founders. It's based on a lived
non-hypothetical experience. It's directly relevant. It's actionable.
The YC office hours sessions provide the current cohort a list of the people/companies
that will be attending. They specifically request who they want to talk to. On the
flip side the mentors get a list of the people that wish to speak to them. It meant
before arriving at the YC office as a mentor I could do the requisite background reading on the
startup, their product, their team. What problems did I expect them to encounter? What
were they already doing that really impressed me?
When we met there's almost no time wasted setting context. "Do you get what we're doing?
Ok, good. Here's a challenge we're facing. How would you think about...?"
## And on the horizon he saw...
I've been to the future. I've seen what it holds for these want-to-be-as-good-as-YC incubators
and accelerators.
It's more focus.
Instead of a mismash of products, all serving different markets, they're part of the
same supply chain. It's a collective of products that are complimentary. That solve adjacent
problems within a domain. That leverage each other to multiply the benefit provided
to an end customer.
Demo days are no longer just passive show and tell.
> "Here's what our customer research and validation told us about X...", "Hey that's great
> insight because they're _our_ customers too!".
Everybody stops repeating the same old mistakes because you learn them as a group. Your
peer companies become your biggest advocates and your highest value sales channel. Trying to
find investors that "get it" becomes easier, because most of you share the same investment
partners.
They see the big picture. They share the big vision.
## It's been done!
This isn't some amazing prophecy, at least not on my part. I've seen this place. I had
the good fortune of working amongst it for a year.
It's called [Heavybit Industries](http://www.heavybit.com).
I assume most of you won't take the time to actually digest the wealth of content on the
site so let me break it out for you.
### The members
[Look at this list of companies](http://www.heavybit.com/members). You've probably heard of most of them, you might be
using many of them:
- [Stripe](https://stripe.com/)
- [PagerDuty](https://www.pagerduty.com/)
- [CircleCI](http://circleci.com/)
- [Meteor](https://www.meteor.com/)
- [Keen IO](http://keen.io/)
- [Rainforest QA](https://www.rainforestqa.com/)
- [Librato](https://www.librato.com/)
- [Convox](http://convox.io/)
- [Apiary](https://apiary.io/)
- [Runscope](https://www.runscope.com/)
- [Treasure Data](https://www.treasuredata.com/)
- [Zencoder](https://zencoder.com/en/)
- [Gradle](http://gradle.org/)
### The lessons
There's some lessons and advice that's almost timeless, especially within a constrained
problem space.
- [Is freemium a good idea for our us?](http://tomtunguz.com/perpetual-freeloader/)
- [How do I convince "enterprise" we're legit when it comes to security?](http://www.heavybit.com/library/video/2013-06-11-adam-ely)
- [What's the right headcount balance between sales & engineering?](http://tomtunguz.com/spending-habits-public-saas/)
- [How do I price and sell the value of my product?](http://www.heavybit.com/library/video/2013-07-16-michael-dearing)
- [What metrics and industry benchmarks should I be aiming for in my \*aaS business?](http://www.heavybit.com/library/video/2013-11-12-tomasz-tunguz)
- [What are some effective marketing tactics for reaching developers?](http://www.heavybit.com/library/video/2013-10-22-danielle-morrill)
- [How and when do I grow my marketing team?](http://www.heavybit.com/library/video/2014-07-15-claire-hunsaker)
- [How do create and distribute content for a developer-facing blog?](http://www.heavybit.com/library/video/2014-11-11-iris-shoor)
- [When am I ready to launch?](http://www.heavybit.com/library/video/2015-06-16-melissa-smolensky)
- [How do I maximise the effectiveness of that launch?](http://www.heavybit.com/library/video/2015-06-30-krithika-muthukumar)
- [How do I sell to enterprise customers?](http://www.heavybit.com/library/video/2016-02-09-alex-malinovich)
- [How do I market my product via third-party channels?](http://www.heavybit.com/library/video/2015-01-27-craig-kerstiens)
Beyond that though there's the day-to-day lessons you're all sharing. Because you're in the same physical space. Sharing an
intersection of customers. Living the same day-to-day experience of what the market looks like _right now_ and what's working.
### The acceleration
There comes a time where money can help turn a good idea into a hugely impactful one. Selling to a customer can be hard
because you need them to believe in your product and that it solves their problem. Selling to an investor? You need them to believe that your product solves somebody else's problem.
A problem they may not have any direct empathy for.
That it doesn't just solve 1 customer's problem, that it solves at least thousands. Possibly millions.
It's at least 100x harder.
Unless those potential investors already get it. Because somebody else has sold them on that vision already.
| Company | [A16z](http://a16z.com/) | [SV Angel](http://svangel.com/) | [Baseline](http://www.baselinev.com/) | [Ignition](http://www.ignitionpartners.com/) | [Harrison Metal](https://www.harrisonmetal.com/) | [Data Collective](http://dcvc.com/) | [True Ventures](https://trueventures.com/) | [Streamlined](http://streamlinedventures.com/) | [Kholsa](http://www.khoslaventures.com/) | Heroku Founders |
|:--------------|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|
| Zencoder | :white_check_mark: | :white_check_mark: | | :white_check_mark: | | | | | | :white_check_mark: |
| Stripe | :white_check_mark: | :white_check_mark: | | | | | | | :white_check_mark: | |
| PagerDuty | :white_check_mark: | :white_check_mark: | :white_check_mark: | | :white_check_mark: | | | | | |
| Meteor | :white_check_mark: | | | | | :white_check_mark: | | | | |
| Runscope | :white_check_mark: | | | | | | :white_check_mark: | :white_check_mark: | | |
| Rainforest | :white_check_mark: | | | | | | | | | |
| Citus Data | | :white_check_mark: | | | | :white_check_mark: | | :white_check_mark: | :white_check_mark: | |
| CircleCI | | :white_check_mark: | :white_check_mark: | | :white_check_mark: | | | | | :white_check_mark: |
| Librato | | | | | :white_check_mark: | | | | | |
| Pantheon | | | :white_check_mark: | | | | | | | :white_check_mark: |
| Apiary | | | :white_check_mark: | | | | | | | |
| Iron.io | | | :white_check_mark: | :white_check_mark: | | | | | | |
| Treasure Data | | | | | | | | | | :white_check_mark: |
| Keen IO | | | | | | :white_check_mark: | | :white_check_mark: | | |
| Gradle | | | | | | | :white_check_mark: | | | |
That's a table mapping a subset of the investors that have invested in Heavybit member companies.
Look at all those overlapping investors. Those shared interests.
Do you think [Baseline Ventures](http://www.baselinev.com/) know that companies who are
trying to massively scale out their workload ([Iron.io](http://www.iron.io/)), want to use CI ([CircleCI](http://circleci.com/)) to ensure they're not scaling out a broken
change across that platform, and need their ops team to know immediately if it does break ([PagerDuty](https://www.pagerduty.com/))? Of course they know that!
[Harrison Metal](https://www.harrisonmetal.com/) get it too. And they realised those same companies need visibility ([Librato](https://www.librato.com/)) into what exactly is
happening on their production systems.
## Bringing it home
Australia's financial sector has been one of the strongest globally over the past decade. It was largely immune to the catastrophes and collapses other economies faced.
Watching from afar it felt disappointing that we weren't parlaying that success into further innovation within the sector.
Then I discovered [H2 Ventures](http://www.h2.vc/). A fintech focussed investment firm.
In such a highly regulated sector it makes sense to have the support of people who can help you navigate that regulation _quickly_. I'm sure it would help to
surround yourselves with other companies who are navigating the same issues at the same time. Enter [Stone & Chalk](http://stoneandchalk.com.au/). A space
to house all those fintech companies.
Sound familiar?
The thing is it's not just our financial sector that's had success. Look at the success stories coming out of our [burgeoning tech startup scene](http://glenngillen.com/thoughts/australian-startup-ecosystem):
- [Atlassian](https://www.atlassian.com/)
- [Campaign Monitor](https://www.campaignmonitor.com/)
- [Envato](https://envato.com/)
- [99designs](http://99designs.com.au/)
- [Canva](https://www.canva.com/)
- [BugHerd](https://bugherd.com/)
- [Buildkite](http://buildkite.com/)
- [Hava](https://hava.io/)
- [Calibre](https://calibreapp.com/)
- [Freelancer](https://www.freelancer.com/)
- [Sitepoint](http://www.sitepoint.com/)
- [DesignCrowd](http://www.designcrowd.com/)
- [OrionVM](http://www.orionvm.com/)
- [Kaggle](https://www.kaggle.com/)
- [Flippa](http://flippa.com/)
- [MetaCDN](http://www.metacdn.com/)
- [Pin Payments](https://pin.net.au/)
They run the gamut of the supply chain for modern developers. We're building the tools for the people who build things. And we're building great ones.
Imagine the power multiple applied when companies like this all lived under the same roof.
## Lets make it happen
I'm looking forward to seeing the profits already realised by some of these companies re-invested into making it happen.
Assuming the recent tax incentives for foreign investors doesn't inspire one of them to do it here first.
## Your talent isn't expensive
I've spent much of the past two years speaking to startup founders. Drifting from
coffee to coffee, mostly speaking to people in Melbourne. Talking about what they're doing,
how they're doing it, what's working, what's not, and what they wish was different.
I offer help where I can, but the truth is people already know what they need to do. They
just need to start doing it. And sometimes a coffee with a stranger, publicly admitting
they need to change their actions to match their priorities, and the threat of someone
calling them a month later to hold them accountable is enough to get things moving.
The greatest disparity I see between what people "know" and reality continues to be two primary
areas: hiring and money. It's often intertwined with the difficulty of running a startup in
Melbourne versus somewhere like San Francisco. We don't have the same talent pool. People here
are expensive. The lack of a mature VC industry means we don't have the capital required
to grow at the same speed.
## The grass isn't actually greener
Every single one of the conversations I've had has exposed a massive gulf between
the perception of a gold rush of the worlds-best developers queueing up for employment in the Bay Area
and the utter dearth of employable people, and the feeding frenzy of employers trying to hire them.
I'll start with a single example that got a lot of coverage recently: [a talented person with no
commercial dev experience on a USD$250K first year salary](http://haseebq.com/farewell-app-academy-hello-airbnb-part-ii/).
In Australian dollar terms, based on today's exchange rate, that's $329K. It's important
to note here that this isn't some outrageous outlier. Yes, this is someone who negotiated well. But
it's also within the upper bounds of what could be considered "normal" in the Bay Area right now.
So before we dig into the market dynamics that have made this possible, let's get honest with ourselves:
- There is unlikely to be a single developer with _no commercial dev experience_ in all of Australia
that is earning $329K/year.
- There will be very, very, very few full-time employed developers of any skill level anywhere in
the country commanding that kind of salary.
- If companies are willing to fight like this over someone with no experience, what do you honestly
think the market is like there for experienced people?
When you're dreaming of a San Francisco-inspired startup ecosystem, be careful what you wish for.
## High demand is demanding
So how did it come to this? If you read Haseeb's story above then you'll know it all escalated
once Google got involved. Which is the natural progression with most candidates. Or Facebook. Or
Uber. Or AirBnb. Or some other startup that is better funded than you, more exciting than you, can offer
more financial inducements than you, has better organic yoghurt in the fridge than you,
looks better on the resumé than you.
And that's the reality. That unless you're fortunate enough to be one of the unicorn club hiring
has become an almost impossible endeavor. If you are one of those companies, it wouldn't matter
what city you were based in anyway. For everyone else you're left to try and exert influence via
personal networks to convince someone to forego the riches you can't realistically promise
in return for something smaller and hopefully more personally meaningful.
I know numerous companies that have been unable to fill single senior dev positions for over
a year now.
And the reason companies will fight like this over juniors is precisely because hiring senior
people can be even more difficult. In my time at my previous employer we ultimately had to
relocate people internationally to San Francisco (myself included). Which means completing
a Labor Condition Application with the U.S. Citizenship and Immigration Service to prove
that you've been unable to fill the role with someone already based within the USA. Then there's
H1B visa petition to grant your prospective employee right to travel to and work for you within
the USA. Assuming the visa cap hasn't been met (it has), in which case you need to wait until
April 1st next year when the new allocation is dished out on a first-come-first-served basis.
And hope that you're prospective employee is one of the lucky few processed in the 7 days
before the cap is hit again.
Have we talked about the costs of all of this? The fact that you're probably making a > AUD$260K
base offer because this has to be a "senior" person to meet the H1B requirements. Plus the signing
bonuses, stock, etc., so another AUD$240K using the same relative amounts from the earlier
example. AUD$9K in immigration fees, anything from another AUD$2K to infinity in attorney
fees to process it. AUD$30K in relocation expenses. And up to 51 weeks of waiting and opportunity
costs associated with hoping your person actually gets in.
And we haven't even covered all of the flights, hotels, and other reimbursements for all
of the candidates you ultimately had to fly in to interview and not even offer a job to.
You're quickly looking at well north of AUD$500K to, maybe, have someone start next year.
## The local market
I've asked around, the general consensus is that someone with limited commercial experience
would be very lucky to get $60K/year here. So 80% less than that AirBnb offer. Indeed, in
my own looking around and wondering if I should get my hands back on the tools it seems the top
end of the market for experienced people is somewhere around $160K right now.
Expensive little old Melbourne. Where someone with
over 20 years experience get paid 50% of what someone with no experience can earn in San Francisco.
Tough times to be doing business. We should do something to make us competitive. Maybe [give employers up to 45% of their
staff expenditure back to re-invest](/thoughts/abcs-of-startup-financing#rd-tax-grant)?
That'd make Australia 90% cheaper in real terms than the Bay Area equivalent. Or maybe we
should [co-invest dollar-for-dollar in your business](/thoughts/abcs-of-startup-financing#accelerating-commercialisation)?
So you manage to invest $30K yourself into the business, and you get the accelerating commercialisation
grant which dollar matches enough to let you employ that $60K person. And then the R&D grant
gives you $27K back. So in real terms you're only out of pocket $3K on that $60K salary.
Or it's cost you **99% less** to hire someone in Australia, even at the top end of the realistic
salary range for a role, than it would to employ that same person in San Francisco.
## Okay hiring isn't so bad here. What about fund-raising?
Sure. You'll probably be able to raise 10X more there. Where do you think it's all going to get spent?
Just get the job done here, and you don't need to set fire to money.
# Addendum
I've had a couple of people contact me about this post with "yeah, it's not just the money.
Just finding people is hard". Those that know me will know I've _never_ recommended a recruiter
to them before. And I've written in the past on how [I'd rather
spend 3 weeks of my own time vs engage a recruiter](/thoughts/on-hiring) to find a candidate.
If you're in Melbourne or Sydney I'd say just hit up [Matt Allen](https://twitter.com/mattallen)
and Lookahead Search. Having actual developers finding candidates for you, and people that
are actively involved in startups day-to-day, apparently makes a world of difference.
Otherwise treat it like finding customers. You need to hustle. Get to meetups, all of them.
Get to conferences. Get everywhere would-be employees might be. And talk to them.
## Setting up multiple YubiKeys + git signing on macOS
Over the past few years, as an industry, we've all become a lot more aware of supply-chain
risks with the things we're building. In particular, how humans are often the
weakest link and that a well targeted phishing attempt can grant some remote person access
to a system, and from there they can continue to escalate their access until they reach
something of value. I've generally tried to err slightly on the side of paranoid about those
risks, but my latest job means that more than ever I need to take as many precautions as possible
to keep myself (and our customers) safe.
## The 'what' and 'why' for YubiKeys
A [YubiKey](https://www.yubico.com) is a hardware device that you use as an additional
factor to verify you are who you claim to be. If you're at all familiar with any existing
multi-factor/two-factor authentication approaches, like where you use an app or receive a code in an SMS,
these devices solve a similar problem. The difference being that they're generally plugged into
your USB port rather than requiring you to open an app on your phone. Some models are
very slim and are designed to be permanently plugged into your laptop.
"But hang on, if it's plugged into my laptop all the time doesn't that defeat the purpose?
If someone steals my laptop then they've stolen my second factor too!" — Someone (probably)
Something you need to
consider with the security solutions you're adopting is exactly what threats you're trying
to protect yourself against. Someone physically stealing my devices is definitely a concern,
and one that I hope is mitigated by the use of strong local passwords, disk encryption, aggressive
auto-locking of my system if I'm idle for a minute, etc. so I'm not adding YubiKeys into my
process to strengthen that. Rather it's there to protect me from someone who isn't physically
near me but is trying to impersonate me to gain access to a system. Maybe they're trying to
brute force their way into my account somewhere? Have they somehow managed to actually work
out what my password is? Are they trying to submit code changes to a repository I have
commit access to by using my email address? YubiKeys are the way that all these things can be
blocked until I push a little button to confirm I'm the one trying to take that action. All that
said, we're going to try and further mitigate the physical risks too by setting PINs on our
keys.
It's just another layer of defense. One that is difficult to clone or impersonate, while still
being quite convenient.
## Getting a YubiKey
For a start, don't get 1. You should probably always have a backup. I've got 3, a USB-C one
that's permanently in my laptop, a USB one that is at home, and a USB-C + NFC that is portable.
It's much easier to set them all up at the same time rather than trying to add to the collection
incrementally so order whatever you want directly from [Yubico](https://www.yubico.com) in a single
order and then you jump through all the hoops below when they arrive.
## Verifying you've got legit keys
Imagine if you thought your keys had arrived, only to some time later discover all the effort you
went to was wasted because some crafty individual had sent you fake keys and knew your secrets all
along?! I'm not aware of that being an actual in-the-wild attack that's happening but you don't need
to take the risk, as soon as you unbox you new keys you can verify they're genuine at [https://www.yubico.com/genuine/](https://www.yubico.com/genuine/).
## Change the default settings
There's a few default settings we need to change ASAP. [Download the YubiKey Manager](https://www.yubico.com/support/download/yubikey-manager/),
plug in one of your YubiKeys, open the YubiKey manager and change these values:
* `Applications > FIDO2 > FIDO2 PIN` - You'll be asked for this whenever you try to use
the YubiKey to login to a website. Can be up 63 characters, stick to alphanumeric though so
that it will work reliably with anything implementing the WebAuthn spec.
* `Applications > PIV > PIN Management > Configure PINs > Change PIN` - Is used when accessing the smartcard
features of your YubiKey (e.g., logging into your workstation). Between 6 and 8 characters long.
* `Applications > PIV > PIN Management > Configure PINs > Change PUK` - `PUK` = "PIN Unlock", you'll need this
if you exceed the retry limit for entering your PIN. If you lock yourself out of this too you'll have to reset the
YubiKey back to defaults and lose anything (e.g., certificates) you might have saved to it.
* `Applications > PIV > PIN Management > Configure PINs > Change Management Key` - This is used to control access to
this PIV (smartcard) section we've been updating. I'd suggest *not* ticking the "Protect with PIN" box here. It'll switch
the device from using the Management Key to using one derived from the PIN, which in turn means the key changes if you
change your PIN (including if you accidentally lock yourself out). It'll also potentially cause problems if you use anything
other than YubiKey Manager we downloaded earlier to manage your keys. Make sure you know what you're doing, and
the consequences, if you disagree and decide to tick this box.
* `Applications > OTP > Swap` - If you've ever worked anywhere that has mandated the use of YubiKeys you've almost certainly
seen someone accidentally put `cccccbhciggvdudgvidcjhvfnjbvkiubitbkibubrbub` into a chat room. That's someone accidentally
hitting the key and having the code it generated pasted in. If you switch this setting so that OTP touch is configured for
the "Long Touch" you'll need to hold the button on your key down for a full second to get the code. Not much of an
additional inconvenience for day-to-day use, and will all but stop accidentally spamming chat rooms with your codes.
We're going to need some CLI tools installed for the latter steps, so let's get them all installed now:
```bash
brew install gnupg ykman pinentry-mac
```
The GPG interface on your YubiKey has its own PINs and reset codes which are still set to their default values,
but they're not able to be managed via the YubiKey Manager GUI. So we'll need to change them via the `ykman` CLI:
```
ykman openpgp access change-admin-pin
```
Lets do the regular PIN too:
```bash
ykman openpgp access change-pin
```
PINs, passphrases, and Admin keys... oh my! There's a lot to remember here and it can all get a bit confusing later when you're
trying to remember which particular value applies to the question you're being asked to provide. The one that's especially worth
calling out right now is the `GPG Admin PIN` (which can actually have alphanumeric + other characters in it! 🤦♂️), which you set using the
`ykman openpgp access change-admin-pin` above. Whenever we want to load a GPG key onto one of our YubiKeys you'll need this value...
so you're going to need it quite a few times later. Remember that's what I mean when I say `GPG Admin PIN`.
For convenience sake the YubiKey will automatically sign approve signing and encryption requests if GPG
requests it to do so (assuming it's plugged in!). I want the convenience of keeping my YubiKey plugged in,
but I don't love the idea of it just silently approving actions in the background. I'll happily take the minor
inconvenience of having to touch the key to ensure these actions only happen when I explicitly want them to, so
let's change these default settings to a more secure posture:
```
ykman openpgp keys set-touch aut off
ykman openpgp keys set-touch sig on
ykman openpgp keys set-touch enc on
```
If you bought multiple keys (you've got a backup key, right?) then remove the key you just setup and repeat all of the
above steps with your other keys.
## Configuring online services to use your keys
Now we're getting to the business end of being able to use the keys! I started by prioritising the highest value
services (i.e., the ones that would cause the most inconvenience if they were compromised). For me that was:
* [Apple iCloud](https://support.apple.com/en-us/HT213154)
* [Google / GSuite / Workspace](https://support.google.com/accounts/answer/6103523)
* [1Password](https://support.1password.com/security-key/)
* [GitHub](https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication#configuring-two-factor-authentication-using-fido-u2f)
Keep in mind that for all of these you'll want to register _all_ of your keys. This is why having them all available so
you can set them up at the one time is much more convenient than trying to remember to add them in later. Go to your first
service, add your key, remove it and repeat the process with your other keys, _then_ move on to the next service. I primarily use Safari
is my browser, and it took me a while to notice that on some websites when the popup appeared to ask for access I'd have to choose the
small "Other Options" link at the bottom of the popup, which would then present "Security key" as an option. You can see a demo of what to
expect by going through the first couple of steps in the [Yubico demo page](https://demo.yubico.com/webauthn-technical/registration).
Once you've got the first few sites setup it's worth taking a look at the [Yubico setup page](https://www.yubico.com/setup/). Choose your
key type and then click the "Compatible Services" link, it'll show you the other services you can configure. It's an easy way to initially go
through a checklist of services you can setup while you're in that configuration mindset.
## Signing your git commits with your keys
As I mentioned earlier, I'm working at a company that is especially security conscious. One of the requirements is that every commit I make
to any repo I have access to is "signed" to verify it did indeed come from me. It's a good practice to put in place, and for the most part
it's pretty low friction after a one-time investment in setting it up. Unfortunately the default approach to this still leaves a bit to be
desired, as my keys are still on my workstation and so if someone was able to remotely gain access to my machine it'd be easy to make some
commits in the background and have them appear to be signed by me. The signing here is less "signed by Glenn" and more "signed by Glenn's machine".
We can do better.
### Setting up your primary/parent key
We need already installed the gpg tools we need earlier, but if you skipped that bit:
```bash
brew install gnupg pinentry-mac
```
Now we're going to generate a new primary key:
```bash
gpg --expert --full-generate-key
```
The options to select are:
* 9 - ECC sign & encrypt
* Curve 25519
* Does not expire
* Choose a password. Something strong but memorable.
The password you just set is your `GPG Password`. Now we need to grab the Key ID of the key we just generated:
```bash
gpg --list-secret-keys --keyid-format=long
```
You'll see output like the following:
```
-------------------------------
sec ed25519/A9C4550328B2FA3C 2023-01-06 [SC]
048282B36C1D7502380C5E8FB9F3220428C2EA3C
uid [ultimate] Your Name
ssb cv25519/6CD2394A1E1F4C10 2023-01-06 [E]
```
The Key ID is the value after the `ed25519/` part, so in this case it is `A9C4550328B2FA3C`. We'll need
that again later so we'll temporarily stash it into an environment variable:
```bash
export KEY_ID="your-key-id-here"
```
### Adding additional identities
I have a separate email address associated with my git commits, and yet another that I use from my work-issued machine.
If you're in a similar position and need to associate more than one email/identity with you key, this is the time to do
it. Run the `adduid` command from the GPG prompt and answer the questions:
```bash
gpg --expert --edit-key $KEY_ID
> adduid
```
Type `save` to save once you're done.
### Creating subkeys
I'm not going to go into the weeds on how exactly GPG works, other than to say an important part of it is to have separate
keys for encryption, signing, and authenticating. Your YubiKey will store each separately too, so we need to set them all
up now. We'll also set expiration dates on the subkeys so we can rotate them regularly, without having to rotate our
parent key every few weeks:
```bash
gpg --expert --edit-key $KEY_ID
```
You'll be dropped into an interactive prompt within the `gpg` command, where you'll want to run `addkey`:
```
> addkey
```
Choose `(11) ECC (set your own capabilities)`, `(A) Toggle the sign capability`, `(Q) Finished`, `(1) Curve 25519 *default*`,
`3m (3 months)`.
Now we'll do the same for a signing key:
```
> addkey
```
Choose `(10) ECC (sign only)`, `(1) Curve 25519 *default*`, `3m (3 months)`.
`save` all those changes and quit GPG.
### Exporting your keys
```bash
gpg --armor --export-secret-keys $KEY_ID > parent.key
gpg --armor --export-secret-subkeys $KEY_ID > sub.key
gpg --armor --export $KEY_ID > public.key
```
We'll need the Key ID in a easy to reference format for future sessions (i.e., if we need to replace a lost YubiKey),
so lets save that detail in a local file too:
```bash
cat $KEY_ID > key_id
```
Now you'll want to zip that file (and encrypt/add a pssword) and store it somewhere safe:
```bash
zip -e yubikey-gpg-keys.zip keyid parent.key public.key sub.key
```
### Configuring GPG to use your first YubiKey
Now we can get on to moving our keys onto the YubiKey! We start by opening our key in edit mode:
```bash
gpg --expert --edit-key $KEY_ID
```
And then we tell GPG to move the key to our card:
```
> keytocard
```
Choose `(1) Signature key`, it'll prompt for the password you put on your GPG key earlier (your `GPG Password`),
then it'll ask for the `GPG Admin PIN`. Next:
```
> key 1
> keytocard
```
Choose `(2) Encryption key` and answer the password/PIN prompts as before.
```
> key 1
> key 2
> keytocard
```
Choose `(3) Authentication key` and passwords/PINs again.
Finally save and quit:
```
> save
```
### Repeating the process with multiple YubiKeys
Now we need to take that backup we made of our GPG keys earlier, and use it to bootstrap our other YubiKeys.
We're essentially forced to reset GPG back to a state of finding the keys on disk (which is why we needed the
backup), and then have it move those keys onto the next next. In doing that though, it'll remove the keys from
the disk. So you'll repeat this restoration process for each subsequent key you need to setup:
```bash
killall gpg-agent
export GNUPGHOME=$(mktemp -d)
cd $GNUPGHOME
echo "pinentry-program /opt/homebrew/bin/pinentry-mac" > gpg-agent.conf
unzip /path/to/yubikey-gpg-keys.zip
gpg --import parent.key
gpg --edit-key $(cat keyid)
```
Note: at this point I initially kept getting errors saying `gpg import from failed: No such file or directory`.
That error was from doing the work above in a temp directory and not having the `pinentry` config in place. Adding
this note to try and help any others who might end up here via search results.
Now to move the keys to the card:
```
> key 1 (it should put an asterisk next to the subkey that has "usage: e", for encryption)
> keytocard (choose encryption, 2)
> key 1 (deselects/removes the asterisk)
> key 2 (it should put an asterisk next to the subkey that has "usage: sa", for signing + auth)
> keytocard (authentication)
> key 2
> key 3 (asterisk next to the subkey that has "usage: s", for signing)
> keytocard (signature)
> key 3
> save
```
Done. Remove your YubiKey and repeat with any others you want to setup.
## Securing git operations on GitHub
We're going to need some of those files from the backup again, this time it's the `keyid` and `public.key` files.
From the machine(s) where you're committing code run the following:
```bash
git config --global user.signingkey $(cat keyid)
git config --global commit.gpgsign true
gpg --import public.key
```
Then go to your [GitHub > Settings > Keys](https://github.com/settings/keys) page, click "New GPG Key", and paste
the value of `public.key` into the `key` box.
Now when you commit changes you'll receive a prompt to enter your GPG PIN. If you enter that successfully everything
will just seemingly come to a complete stop, but your YubiKey will be quietly sitting there flashing it's little green
light essentially asking you to complete the operation. Hold your finger on the YubiKey touch pad and it'll sign the commit and
you're done. I point this out specifically because 1) it's not _entirely_ obvious and 2) if you're doing a batch operation
(e.g., rebase) you'll only be prompted once for the PIN but **you'll need to touch the YubiKey for each commit** it needs to
touch. So keep an eye out for the green light when you're making change to ensure you're confirming when required otherwise
you'll just sit there indefinitely waiting for things to complete.
## Committing changes after switching keys
If at some point in the future you try to use one of your other YubiKeys you'll get an error such as:
```
Pinentry Mac
Please insert the card with serial number:
XX XXX XXX
```
That's because GPG isn't expecting you to switch them about so freely. You can fix that by adding the following as
a function in whatever local shell configuration you have (e.g, in `~/.zshrc`)
```zsh
function switchyubi() {
rm -r ~/.gnupg/private-keys-v1.d
gpgconf --kill gpg-agent
gpg --card-status
}
```
And then any time you switch keys and/or get the error above you can run:
```bash
switchyubi
```
## Recapping on all the good things we've accomplished
You could probably speed run through this in about 15 minutes for 3x YubiKeys once you know what you're doing (i.e., you've done it 6 times in a row
to make sure you've got the details right while writing a post like this 😉). In reality, you should allow an hour or so while you
try and understand exactly what is going on and the swearing that might ensue. But it's definitely worth it! Because:
* Now all of my commits to GitHub can be signed and marked as verified
* We've all got confidence that at the time those commits were made it was infact me (or if you want to be pedantic: from a device that was configured to auth as
me + had one of my yubikeys plugged into it + had a person touch the yubikey to confirm presence + that person new the PIN/password on my GPG key)
* None of my private keys are stored on my laptop any more
* Nobody can impersonate me and push verified commits to GitHub from a device that doesn't have my YubiKey plugged in
* If someone were to remotely gain access to my machine, it's incredibly difficult (I'm reluctant to claim "impossible") to use my keys to impersonate me
Hopefully you've found this useful and can use it to quickly get your own keys setup. We've all got our own small parts to play
to help keep the systems we build, and the people who depend on them, safe from attack.
## A sensible approach to compensation for remote teams
I've recently joined a new [startup](https://www.ockam.io/) with some old friends. Matt
(the founder & CEO) and I were on the same page about so many things when we started talking about potentially working together again. Having seen the inside of a number of companies over the past decade it was interesting how to see how similar our lists of "this approach is broken" and "this works well" were when it comes to building and growing a company.
So while Ockam is in an interesting problem space, that [already has an impressive community and traction](https://www.ockam.io/blog/how_grow_popular_open_source_github), and it's exciting to be back in the deep end at an early stage company again there's another small reason I joined. One that people don't talk openly about very often: Compensation. In particular, Ockam's general approach to it.
But before we get to that let's have that open talk about how compensation is usually approached. Because unless you've been on both sides of the table there's a good chance you've not thought through the incentive structures of all the parties involved or the cascading affects of decisions.
## Real-talk about how your employer approaches compensation
Every company I've ever worked at that's large enough to have a dedicated team (even if it's only a single person) that's responsible for compensation planning has taken basically the same approach. If you're at a company with more than a few hundred people then it's highly likely this applies to you whether you realise it not (unless you're in a sales role. People with commission structures should ignore everything I say).
Let's start with what might seem obvious, what is "compensation"? It's what you get paid. Duh! Your compensation is then typically a mix of your base compensation, probably an equity grant, possibly a sort-of-guaranteed regular bonus, maybe a hiring bonus. Let's call the aggregate of all of these your "total compensation".
This is where the specifics can vary wildly between companies, and even between roles within a single company. The ratio between each of those components is going to be very different. How much each increases during a promotion cycle will be different. Whether they treat them as indendent dials to turn for specific individuals, whether they bias towards one for certain types of incentive/retention, or whether they look at the aggregate and try and keep the total in a specific band. All that's important for the sake of this essay is that the general shape of how things are done will be pretty similar across most companies. They'll just vary which parts of your compensation they apply this approach to.
### There's only so much to go around
The inescapable truth is that at any point in time your company has only got so much they can afford to pay people. They've either raised investment, and the more they pay people the shorter their runway will be. They've taken on debt and the more they draw down on it the higher the interest costs are and the more they ultimately have to repay. They're paying out of cashflow, often slightly more than is coming, with the expectation that spending it now means more will come in later and it's all worthwhile. Or there's profits in the bank and you're drawing down on them, meaning you can't spend them on other things. These might also seem like very large numbers! Even so, it's finite. Everyone has a budget they have to work within.
### Balancing "fairness" with compensation efficiency
Time to be blunt: compensation is entirely a commercial transaction. Your company might talk about it in terms of paying you fairly, individuals (especially in smaller companies) might genuinely believe that. It's not what actually happens in the aggregate though. Paying you "fairly" is about paying you enough to reduce the probability you'll leave. Compensation efficiency is a more serious way of saying "pay them as little as possible".
Sounds horrible when you say it like that, right? We should unpack why it's not people being as horrible as that makes it seem. Remember that budget people have? For the sake of simplicity lets keep the numbers nice and round: there's an additional $100K in the budget to spend on compensation over the next year. There's currently a team of 10 people, all earning $100K. How do you spend the $100K? As that team's direct manager you probably want to do right by your team. They've been doing great work, you want to keep them, inflation is super high at the moment, they all deserve a 10% pay rise. You give them all the pay rise, that's the whole budget gone. But you know there's only going to be _more_ work to get done next year. Does giving everyone a pay rise also mean they're suddenly going to be able to find a way to get 10% more done too? We've already acknowledged they're performing well, there's probably not a whole lot more we could reasonably ask from them. So how does the additional work get done? If we didn't give anybody a pay rise we could use that $100K to hire another person onto the team. That'd help get the work done. Not an immediately great story for the morale of the current team though, they'll be bummed. That'll probably incur a productivity hit. More than the hit we take burning people out asking them to somehow squeeze more work into their day? Hard to say, it might net out. Will people be so dissatisfied about the lack of pay rise they leave? Maybe. More than those that would leave from the burnout case? Who knows with these hypotheticals, maybe it's the same outcome? It's pretty easy to come to the conclusion that the better outcome here is more people, which means paying less. The company is also spreading their risk/bus factor if they do that. The incentive here is to make the money go as far as possible.
Hey, I never promised I could make this sound appealing to you! It's not great thinking you're on the receiving end of this. But this is the #realtalk section. The "company's" interests are in ensuring the company survives as long as possible. It's not about paying you as much as you think you deserve. Those two things are usually at odds with each other. The sooner you accept that it's all just business and nothing personal the better. Ironically, realising it's not personal should make it easier to advocate for your own interests. The pushback you receive isn't necessarily about _you_ specifically, so don't take it to heart. You absolutely should still maximise for getting paid what you feel you're worth as soon as possible, but we'll get to that later.
Paying people too little has its consequences. The company will find it more difficult to attract the type of people it wants to hire. It will struggle to keep the people it already has if they're getting more competitive offers from other companies. It may have a negative impact on motivation and productivity will take a hit as a result. If someone leaves, there's a significant impact to the business. The obvious reduction in output from having one less person, all the time and energy and focus diverted from many others to hire a replacement, the lag between the person leaving and a replacement both joining and then being ramped up and able to contribute. All of these are very negative impacts to a company. So they'll want to minimise them as much as possible. One way (out of many) to minimise the probability of these situations occuring is to pay people more.
But let's be clear: your employer is incentivised to push as close to the line on this as possible. Not because they're jerks, just because it's the most efficient thing to do.
### The promotion & review cycle
At least once a year, for some companies two or three, there's the promo & review cycle. You'll do the dance of making your case for why you deserve a promotion to a new more senior level, or maybe just a pay rise. Maybe you'll have to build a case with support from some peers. There will be a 360 feedback process. Maybe there's a system where "stakeholders from across the business" will be asked to provide feedback to direct your manager, which they'll aggregate and share any themes that emerge. Done well, all this can be quite beneficial at helping you grow and become more effective.
It's got basically nothing to do with your compensation though.
"Whaaattt?! But it's the promo cycle time! That's the time the company works out pay rises! They've _told us_ that! You're wrong."
I've got news for you: if you're getting promoted to a new level it's got nothing to do with the documentation you spent two weeks writing to make your case. Or the half dozen paragraphs of supporting text collected from a handful of peers. It's the year or two of work preceeding all of that. The paperwork is to make your bosses' case easier to support a decision that is already made. Though it wont necessarily be clear to your boss either in that moment _what_ exactly that decision is.
Because months prior a bunch of accountants would have run the numbers and come up with... a budget. Remember we spoke about one of those earlier? There's one here too. How it gets applied will vary from company to company, based on market conditions, and again may be applied either to individual aspects of compensation or to the total compensation amount. I'll again make up some figures, but based on my experience they'll be within a realistic range. Each business area will be given a total budget, and typically some guidance on how it should be deployed. Labels like "high priority retention", "at level", "low urgency", and "non-regrettable attrition" will get thrown around. Basically clinical ways to discuss the spectrum of "people we want to keep" to "no big loss if they leave". The guidance will then be something like 8% compensation increase for "high priority retention", 4% for people performing "at level", and possibly nothing for anything below that.
Then the reviews and calibrations begin. Someone, somewhere, is going to grade everyone on a curve. Maybe it's not your manager, it might be their manager, maybe it's the head of finance or the CEO. But I can assure you there's not going to be a situation where all the management submissions come back, everyone is graded as "high priority retention, please give them an 8% pay rise", and the company turns around and thinks "oh, we incorrectly budgeted this. We're going to have to pay everyone a lot more than we expected". Implicit in the budgeting decisions was a model with some kind of Guassian distribution of pay rises. How many promos and pay rises could be given out was decided long ago. Way before the performance appraisals came in. By a bunch accountants. Remember it's not personal, just business.
### Pay scales and indexing
You may have encountered pay scales before, in various jurisdictions or sectors they've had to be made public. Other times companies talk about them under the guise of being transparent and ensuring fairness. How true that is really depends on how they're deployed.
I'll start with the positive implementations. In many public sector roles there are very strict and narrow bands for compensation, that are publicly available and usually published with a job posting. You know upfront either the exact amount you'll get paid or a tight range it will sit within. No or little negotiation possible. No or little risk of bias in the process re-inforcing systematic discrimination.
Most companies, even at their most bureaucratic, can't match the public sector for that level of inflexibility though. So the pay scales exist mostly as a starting point, or worst case as a justification for why they're unfortunately unable to adjust compensation to meet your expectations. When a job gets posted an expected level will get assigned, that'll initially get used to help calibrate screening for suitable candidates. Then you'll spend weeks or months interviewing people. Finally you get to the offer stage, but the candidate decides to negotiate hard. The compensation they're asking for is outside the range originally assigned for the role, and you're confident they'll walk from the current offer. Does the company let them walk and have a half dozen people spend more months trying to find someone else? Of course not. They come back with an offer that will be accepted. Either by offering above the range or through title/level inflation (i.e., giving someone a more senior role than their experience would suggest their equipped for, just to keep them in official comp ranges).
So how exactly are the ranges for each level determined? There will usually be some third party involved. A company, analyst firm, or similar that generates reports on salaries. The company will target a certain percentile amongst a group of "peer" companies. Let's say "70th percentile". That means they're aiming to be in the top 30% of companies in terms of compensation. It all gives a good air of impartiality and independence. "We're not just making up these numbers, we're just using a third party firm and then making sure we're near the top". Not at the _very_ top though. It's about finding that balance where they can pay enough to attract people while simultaneously paying as little as possible. There can be a lot hidden in the finer details on the definition of "peer companies" too though. What constitutes a peer company? Are specific companies being excluded? Are you tracking base salary or total comp? I've seen two basic modes of operating here: very generic and simple or customised. Generic and simple is usually because it's quicker and cheaper to just have a basic framework and have everyone get back to work. If it's customised it's because there's real economic value to the company for being able to tweak the definitions to find the most favourable interpretation of the numbers. Lies, damn lies, and statistics.
### The geography overlay
Just for an extra bit of fun, companies will usually throw a geographic adjustment to the pay bands or actually index each role to the pay ranges the research firms report in each local market. Again there's a pretty bimodal approach to this in my experience: simple or sophisticated.
The sophisticated approach is to get real granular. Individual states, maybe even individual cities in some places. Pay based on what the local market pays for that particular role. The rationale will often be something like "we can't skew the local market, we need to be in line with peer companies within a region". I get the impression people who've told me that line genuinely believed it themselves, I think some people are just especially comfortable swallowing the company line uncritically. It's nonsense. Tech salaries in Seattle are as high as they are _because_ Microsoft and Amazon are there. Companies have absolutely no problem skewing the local market when they think it's in their interests to do so. This has never been about some noble desire to slow the roll of gentrification and late stage capitalism in our suburbs.
No, it's about the same efficiency everything else here has been about. So if a "Senior Engineer in Seattle" earns $200K/year and a "Senior Engineer in Vancouver" earns $150K/yr the company will pay $50K/yr less for the person in Vancouver. Hey, it makes good business sense. Nothing personal. If you can get the same job done by someone who is $50K/yr cheaper just because they live 150mi further north you'd take that deal too.
I find a far more insidious aspect of this though that the super fine-grained resolution, applied over the top of anything from 5 to 12 levels, across multiple functional areas, manager and individual contributor tracks... it makes any pretense that it's about fairness and consistency disingenous. When you've got >3000 individual "salary ranges" there's plenty of places to find whatever justification you want.
### Individual maximisation
Ok, this might all seem a bit grim. It was meant to be a bit of an exposé of how many companies handle compensation because not enough people have seen the inside of the process. And while I've painted a picture of a system that isn't optimising for the individual and where decisions that impact you are made far away from your influence. Don't dispair! It might not optimise for the indivual but _you_ absolutely still should. So before we wrap up this way too long setting of context and move into talking about what Ockam did different that caught my attention, let's talk about individual maximisation. Let me introduce Sarah, she's a composite character of actual circumstances and events I've witnessed as part of hiring and compensation planning teams over the past 15 years...
Sarah is a very talented engineer. She's managed become very accomplised with a very niche technology, but one that's become critically important to handful of companies that are doing very sophisticated things in this new "cloud" thing. She's interviewing at a few companies in the Bay Area and gets offers from all of them. Very good offers! But there's one in particular she really wants to work for, an exciting startup, but unfortunately it's the lowest of the offers. She's transparent about that and says the disparity is just too much. The startup is desperate though, there's just not many people with this expertise in the market right now. They decide that despite only having a couple of years experience they'll hire her as a "Senior Engineer" to get her offer in line with what she's looking for. Success! Everyone is happy and gets to work.
Within a year the startup is acquired. Not really long enough for Sarah to have vested much equity, but she's loving her work so she's not bothered by that. And the acquirer is pretty hands-off apart from standardising on some of the HR and similar processes. One of those standardisation aspects is the approach to compensation and promotions. Despite coming in more senior than she probably should have she's doing a great job. Maybe still not quite at what they'd typically hire as a senior, but far too good to risk losing. So in the promo cycle her manager flagged her as "high priority retention" and the good news is it's boom times and the parent company had a great year so there's a tiny bit more to go around than usual so she gets a 10% pay rise.
She's growing weary of San Francisco though, most of her team is spread across North America anyway (the startup had a largely distributed team from the start). She decides she's moving to Great Falls in Montana. She makes sure her boss is cool with it, he is, but he mentions that the new owner of the company indexes to the local market. There's not really a local market for what she does in Montana though and so when her manager speaks to HR about it they inform him it'll be a $100K/yr pay reduction to her. He passes on the bad news. Sarah informs him the pay cut isn't going to happen because she's going to be just as productive working with her remote team from there as she is from San Francisco. He agrees, and they decide to just not talk to HR about it again. She moves and keeps her salary.
Next promo cycle comes around and suddenly people realise that Sarah's address and comp band don't line up. When they adjust her to the Montana salary ranges she's wwwaaaayyyy off the charts. There's an emergency meeting with Sarah, her manager, and the compensation team to try and reset her expectations. "We're terribly sorry, but you're going to have to be ready for a significant reduction in your compensation. Or you'll have to reconsider moving back to San Francisco". Unfortunately for the compensation team they're outmatched by someone who has grown in both confidence and competency over the past 2-3 years with the company, "If I move back to San Francisco, I'll keep my current compensation?". "Correct". "Thanks, I just wanted to be clear that this has absolutely nothing to do with what you're willing to pay me. You seem to be under the impression that there is some market for people with my skills in Montana, and that you could hire my replacement from that pool. If there is you should absolutely hire those people immediately. There is not. Not only is there not a pool of these people in Montana, there's no pool of them globally. There's about 6 people in the world who know how to use this technology to this level, and the other 5 are already on my team. If I quit you're either stuck with a team of 5, or you have to hire me back". Well played, Sarah. The double-edged sword of giving so much back to the open source community is you can rapidly accelerate someone's development to the point that they're now a core contributor to some of the most critical software your company is running on. Compensation teams hate this 1 weird trick!
Not only does Sarah keep her current salary, not only is she once again "high priority retain", but she's been growing in confidence and influence. Lots of really, really positive growth. She's definitely performing at the top end of senior now but she's been here almost 3 years at the same level and so her manager had steeled himself to push for her promo. A confluence of fortunate events happen for Sarah: there's been a whole lot of attrition recently. Lots of the early employees have left in recent months given they're fully vested, which has meant both lots of backfill alongside all of the growth hiring. That means there's _a lot_ of employees who've been at the company less than a year and so aren't eligible for promo. Her boss had built a pretty compelling case for the promo and thought the areas where she excelled would hopefully hide the gaps that still exist. He probably didn't need to bother though. There's so few people up for promo that it went through unchallenged. The system doesn't know how to deal with individuals, it just expects a certain percentage of people to go through the process. She made it through.
For the comp ratios to work again HR have had to do something in the backend to keep her on the Bay Area salary bands. And now she got a promotion to "Staff Engineer". She didn't just keep her salary, she came away with another 10% pay rise! As well as a very generous stock refresh.
Every time compensation is discussed manangers and HR go to great lengths to explain to Sarah how out of band her compensation is. "We index to the 70th percentile in the local market, and your current compensation is way above that. I need to stress how little flexibility we have in terms of any future compensation increases". It's a story she's heard dozens of times. It really ramps up around promo season. But she continues to do exceptional work. She continues to get flagged as someone they can't afford to lose. And so she continues to get the "high priority retain" compensation increase each year. Because the system doesn't know how to discriminate.
**Fin.** _(Of Sarah's story)_
This is obviously a golden tale about a high performer. If you're performing and in-demand though it's in your interests to maximise the things you can influence. Because maximising those means that when the system works in your favour the effect is amplified. Think about the compound interest on all those decisions and events. Influencing the initial negotiation to get the higher comp (and level), the system them amplifying that in the promo cycle, holding strong on the regional adjustment, system amplifying, etc.
For many tech workers the difference can literally be hundreds of thousands of dollars over just a few years. A delta that only grows over time.
Whenever, wherever, and however you can make sure you get paid.
## Ockam's compensation approach
Whew! That was a lot.
After all of that this is probably going to be pretty boring because of the simplicity, and in my opinion, the sensibility of it all. As a hiring manager I found the huge disparity between salary bands in different regions infuriating. It made no sense that I'd have to consider giving someone in a low compensation market an inflated title just to get them what they asked for. Meanwhile we'd have another candidate in the pipeline who was more junior but would be paid _more_, even on a lower title, than the first candidate because they were in a high compensation market. But big companies need complicated systems.
Then there's the various aspects of Sarah's story where it becomes obvious that there's "the rules" and then there's reality. And the rules only apply to some people. Turns out that almost all of them are negotiable for the right people. The definition of "right people" is in part people who are in high demand and have leverage, but more than anything it actually meant "people who knew they could negotiate an exception for themselves".
So when Matt & I finally sat down to discuss compensation at Ockam he said "I think you'll like this", and he was right. It felt like we'd observed the same anti-patterns and special exceptions and he'd decided to both simplify it and make those exceptions apply to everyone so they weren't exceptions any more:

For a start they index at 75th percentile, so far pretty standard. But the index is only of private SF/SV from the Carta benchmark report, a simple approach that skews toward the top end of actual peers rather than being creative. Localization, but a completely tractable one. It's not over 50 discrete states, it's 4 global regions grouped roughly by cost of living. Discount slightly from the SF/SV salary based on the reduced cost of living in other locations. No local market for a Rust engineer in Montana? No problem, the base rate is taken from the SF rate anyway.
How much do you discount though? Well it varies by level. The most junior role has the most discount applied (it still isn't much), and as you become more senior the gap closes. Anyone at the IC5 level or above is being paid at the SF/SV rate. And... I think it just made sense? It's what I'd seen play out time and time again from people who knew they could negotiate. Especially amongst those that accepted a role in SF and then later decided to move and refused to take a pay cut with it.
Which brings me to another aspect of Sarah's story, the need to maximise negotiation outcomes. From the Ockam handbook: _"We aim to have consistency in our compensation across the entire team and remove negotiation bias. Since we are a global company, there are different social norms and expectations for negotiating an offer to join a company. We remove this bias so that as we grow we don't end up with different compensations for different categories of people."_. I've seen the long-term implications of people who didn't negotiate well enough on the way in. It's almost impossible to fix. The compounding impact means that the gap between two genuine peers who negotiated differently ends up only growing wider over time. In reality the only way it gets fixed is the person leaves and gets the compensation they should have been getting at a new company.
Especially given so many companies over the last few years have moved to having distributed teams I'm disappointed so few of them have been proactive in taking a global approach to compensation. I get it though, why bother? If you can keep a complicated system that keeps costs low then why would you change it? Different companies have different priorities though. So I greatly appreciated the fact that Ockam's priorities were around simplification and just getting straight to work. The time and energy we'd spend collectively micro-optimising this stuff is time and energy wasted. It's obviously a point of difference too. A potential marketing and recruitment tool for people who want to see companies take a new approach to thinking about compensation. It worked on me! 😉
The [Team Handbook](https://www.ockam.io/blog/team_handbook#how-compensation-is-determined) that covers all of this is publicly availble. I've linked straight to the compensation section, but it's worth reading the whole thing.