Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
63
Since reading ‘High Growth Handbook’ by Elad Gil, the value of writing a ‘Working with’ document became crystal clear to me. I am sharing mine externally to inspire other founders and leaders to reflect and write down their own working styles. These documents are incredibly beneficial, especially in a multi-timezone, remote setting like we have at RevenueCat. I’ve spent some time fine-tuning mine, and this is the updated version. Welcome to your go-to manual for understanding how to collaborate effectively with me. My Mindset: Logic-Driven, Plan-Oriented I’m a logical thinker, much like a computer. If A implies B and we have A, I’ll typically conclude B. Sticking to plans and predictability is my comfort zone, yet I value reactivity, especially when customer-related issues arise and are solvable. This company isn’t just a job for me; it’s my life’s work. I’m deeply invested in everything here — our technology, culture, team, and customers. I get inspired and energized by hard-working...
a year ago

Improve your reading experience

Logged in users get linked directly to articles resulting in a better reading experience. Please login for free, it takes less than 1 minute.

More from Miguel Carranza

My role as a founder CTO: Year Seven

2024 has come and gone, and it’s time for my annual post. What a year for startups—like squeezing five regular years into one. Do you remember the Apple Vision Pro, the DMA regulation, founder mode, or the o1 launch? All of that happened in just the last twelve months. It’s also been wild at RevenueCat. My journal is full of stories that could fill a whole book or even a few HBO Silicon Valley seasons. Some are inspiring, some are hilarious, and others are honestly gnarly. Due to limited space, the need for context, and respecting everyone’s privacy, I’ll cover only the most interesting topics at a high level. Looking back, it was a good year for RevenueCat. Actually, a great one. Perhaps our best since 2020. We have plenty to celebrate: We accelerated again, and we hit our C10 revenue plan. We made our first acquisition and welcomed an amazing founder to the team. We signed our first multi-million-dollar contracts. We went to more than 20 events around the world… …including hosting our own conference, featuring its own award ceremony. We became the #1 payments SDK on iOS. Our swag went to 11. We launched over 80 user-facing features. We showed up in Times Square and along Highway 101. We raised a mini Series C and welcomed two new board members. We landed in Japan for the first time. Our API grew beyond 2B requests per day. We are processing nearly twice the TAM we had when we started the company. OpenAI is a friend of the cat. We continued building a winning team. We hired people who were on my to work with bucket list even before we started the company. By all the metrics, 2024 was our year of shipping and selling. We absolutely helped developers make more money. My role If you compare our progress to the goals I wrote about in last year’s blog post, it’s clear we succeeded. But the journey itself was a lot rockier than I had imagined. Many things didn’t go as planned, somes hires did not work out, and some strategy changes were really hard to push through. At the start of the year, besides my usual responsibilities, I also set four personal goals to help me scale with the company. I wanted to: Ship code more consistently. Talk to at least one customer every single day. Get more involved in areas outside of engineering. Stay very close to my co-founder Jacob, giving him my full support. I completely missed goal #1. Honestly, that hurts because I love building software. But I’m not too upset about it—our customers care that the team ships, not that I personally do. And in a way, I did help the team ship. As for the other three goals, I hit them, at least based on the company’s results. Still, my self-perception wasn’t always great. I found myself acting much more like a co-founder/executive than a CTO. Some weeks were brutal, with constant context switching across tasks and teams I didn’t always enjoy. At one point, there were over 60 people in my org, spread all around the world, which is pretty intense for an introverted computer kid. A lot of bullshit escalates to the top, making me question my entire existence some days. And then life threw serious personal emergencies at some of our team members too. Like I said last year, life is what happens when you’re busy building your startup. As we got closer to 100 people, it was statistically unavoidable that we’d face a few life-changing traumas—sometimes several all at once. As a founder, you need to be supportive and empathetic, but also protect your mental health. As a human, it’s tough. Add in a couple of two-year-olds who were often sick and not sleeping, and it felt like a ticking bomb. Did I burn out for the first time in my life? I don’t think so, but it got close. I’m confident being a founder (and having a co-founder) kept me going. I care too much and must stay resilient. If I’d been just an employee, I might have tapped out. But enough about the tough parts. Aside from being a professional BS handler, here’s how I spent most of my time: Lots of travel This was the year I traveled the most in my life—and looking back, I probably should have traveled even more. I visited customers, helped with sales, conducted executive interviews, and spoke at a few conferences. It’s hard being away from two little kids, but each trip turned out to be worth it. Support One of my biggest worries this year was our support function. Everything was fine, but our Support Engineering Manager was going on parental leave, and the bus factor was scary. I’ve seen support crises before—they’re not fun. It wouldn’t have killed the company, but at our size, it might have forced us to pull senior engineers into support and slow down our product velocity. Time was short, so we tried a few things that worked: We hired two new Developer Support Engineers who already knew our product—former customers! Their onboarding was smooth, and they hit the ground running. We split the support team into two pods with their own leads. Each pod handles certain tickets, and they collaborate with each other instead of relying too heavily on one person. We finally set up our first on-call rotation for emergencies. Sales and post-sales Reviewing sales and implementation calls, giving technical input, collecting enterprise customer feedback for product and engineering, joining calls, and even doing some in-person visits. Writing more As our team continues to grow across the globe, not everyone has the same direct interaction with me or Jacob as before. A lot of our culture and collaboration style was once passed through observation, but now needs to be written down to reach everyone faster. These days, my code editor is basically replaced by Google Docs. I’ve been publishing more internal and external documents—like our Engineering Strategy and an updated How to Work with Miguel. Product delivery We still have a few details to refine, but our founder Shipping and Timeline reviews have been valuable. It gives Jacob and me a high-level view of everything in progress, lets us offer feedback, and helps us dig deeper where needed. It’s a great way to see each team’s capacity, find bottlenecks, keep a sense of urgency, and deflate anything that’s not truly important for the customers. Re-orgs This year brought a couple of big reorgs in product and engineering. Overall, they went well, but the puzzle gets more complex with each new piece. We created sub-teams to narrow their focus and keep things running smoothly. For the first time, we had a couple of management layers between me and our ICs. One major change was shutting down our Enterprise/Reactive team. The idea was solid at first, and they delivered plenty of value, but eventually they became the random tasks team. Our new plan is to reinforce the rest of the teams: quick enterprise requests go to the right team to handle them reactively, while bigger projects move to the main roadmap (which keeps us disciplined). The engineers from that old team will help bootstrap new teams as we hire in 2025. Hiring Our engineering hiring goals weren’t super ambitious, but we still reached them. The pace was a bit uneven, and some roles took longer to fill than desired. However, when Hiring Managers took ownership of the process (with recruiting as a support) it made a huge difference in the quality of our candidates. We also brought back the founder interview stage: Jacob or I spoke with every single candidate before making an offer, and we plan to keep doing this for the foreseeable future. Random founder stuff Not my main focus, but I still spent a fair amount of time handling people-related topics, operations, investor, customers and partners relations, fundraising, company policies, planning, etc. Learnings: deepened insights On culture Shipping is king. Yes, deadlines are stressful, but failing to ship and getting stuck in endless debates is far worse. It’s depressing. If someone isn’t a good culture fit, it will be more than a single incident. Over time, it becomes pretty clear to everyone. Top performers tend to measure themselves against the very best in the company. Reassure them they are doing a great job. On the other hand, those who underperform will look to other underperformers to gauge their own progress. Even top performers will struggle if they don’t fully align with the vision. Nothing beats talking to customers. Encourage everyone to do it. Better in person. Most managers don’t have a strong incentive to be strict, so finding the right balance takes a lot of calibration. Only once everyone is aligned, you can truly delegate. On hiring Managers hate hiring because it’s binary: a lot of “no”, but eventually one “yes” can change everything. Staying consistent really helps. Simple things, such as weekly updates keep everyone accountable. People management sucks. If someone’s main motivation is to be a manager for the title, or so they can “lead,” that’s a red flag for me. Best managers end up being those who never planned on becoming one in the first place. They actually roll up their sleeves and can do the work. Big ideas are great, but they need to be executed. This matters even more when it comes to executives. A truly great exec can change your life and it will feel like a brand new company. Given their influence, anything less than great will eventually turn into a big mess. Spend time with executive candidates in person. Watch how they work and make sure they’re real builders. Previous founders and true engineers are usually a little bit de-risked, but they’re still not guaranteed. I also like to write a very detailed onboarding doc, clarifying context, expectations, and what success or failure looks like. Having them write a 30/60/90-day plan helps us all align. I’ve learned not to rely on their past pedigree. The real key is their glass eating endurance. On company building Re-orgs are inevitable at a growing startup, and they will feel scary or emotional for people who aren’t used to them. I find it helpful to be super clear about why we’re doing it, and to share the fallback plan if things don’t work out. But I’ve learned that by the time you think you need a re-org, you’re already behind. We now plan to reassess our team structure twice a year for optimal shipping. A numeric goal (like an SLA or revenue target) is just a proxy. Missing it isn’t the end of the world if you learn in the process. But if you surpass it without recognizing what’s broken underneath, you’ll be masking critical issues. Perfectionists are great, but can have a tough time at startups. Some do fine if they’re focused on one specific thing or working as an IC. But as soon as they have to juggle multiple tasks, the chaos can feel overwhelming. Help them embrace it. Things will break. It’s about continuously reprioritizing, and stopping small cracks from becoming big fires. No agenda == no meeting. Synchronous time is expensive. The only exception is the occasional unscheduled call. After you reach around 50 people (especially in a remote setup), documenting every process change becomes crucial. I’ve learned the hard way that simply talking about a new process isn’t enough. Founders can’t talk to everyone all the time anymore, and misunderstandings or gossip can spread fast. Not everyone will read everything, but at least there’s a single source of truth to reference. Best people can stretch quite a bit—they’ll often rise to the challenge. But it’s wise to keep an eye on their limits before they burn out or become a bottleneck. On scaling as a founder A great EA is life-changing. Family will be supportive, but it’s not fair to offload all the stress on them. They will end up feeling helpless. Having a solid co-founder, a network of peers, or a good executive coach makes a world of difference. Founders are the ultimate guardians of the culture. It’s constant work, and it will feel relentless—especially when things are going well and it’s easy to get entitled. The best team members will help uphold the standard, but you cannot expect them to do all the policing. At this stage, it’s stupid not to level up your lifestyle in ways that reduce stress or save time. This might include childcare support, having a second car, investing in a better mattress, or hiring help with housekeeping. The startup is bigger than its founders, and the goal is to continuously remove yourself from the critical path. Still, it’s easy to forget that, as a founder, you literally brought everything into existence from thin air. Imposter syndrome often creeps in when you step outside your core expertise, but if your gut feeling is strong, it’s worth paying attention to. Stay open-minded, yet remember that no one knows the company quite like you do. The future Next year is going to be another big one. We’ll keep shipping and selling, while finally tackling our design and UX debt. We’ll keep investing heavily in our infrastructure. Not just for reliability, but also for real-time data. We want RevenueCat to feel fast, accurate, and easy to use. We’re also upgrading our self-serve and enterprise support, aiming for a truly world-class experience. In many ways, we’re finally seeing the original vision Jacob and I had back in 2017 come to life. We’ll be launching new product lines too, and if we execute well, we will be just a couple of years away from hitting $100M in revenue. We’ll keep building a winning team. We’ll hire about 45 people, 30 in Engineering, Product, and Design. It’s a challenge, but totally doable. As for me, my personal goals haven’t changed much, but my perspective has. Jacob and I used to joke that being happy and winning can’t happen at the same time. But why not? We’re in a privileged position to shape our own path and change anything we don’t like. After a lot of reflection and coaching, I realized I was simply too hard on myself. I was feeling depressed by the constant BS even though we were winning. A hack that helped was working with my EA to set weekly goals and then sending a public update to the full team. It let me see the real progress behind all the drama and back-to-back meetings and stay transparent with everyone. Next year, I’ll avoid meetings before 9 AM, keep an eye on calendar creep, and hold myself accountable to exercise and doing what helps me decompress. I know, it’s obvious. I also plan to travel more. Especially to the Bay Area, which is clearly back again. Meeting up with other founders and customers is always worthwhile. Another thing that made last year tough was having half my direct reports on parental leave for about half of the year. Now they’re back, and I can feel the momentum returning. Jacob has also taken over product again, now that he’s stepped away from directly owning operations and people. Increased shipping velocity has been noticeable. We have all the pieces in place. All that’s left is to keep pushing forward: shipping, selling and enjoying the ride. If there’s one thing I learned in Silicon Valley, it’s that no goal is too crazy if you refuse to give up. I really hope you enjoyed reading this post. As always, my intention was to share it with complete honesty and transparency, avoiding the hype that often surrounds startups. If you are facing similar challenges and want to connect and share experiences, please do not hesitate to reach out on Twitter or shoot me an email! Special thanks to my co-founder Jacob, my EA Susannah, the whole RevenueCat team, and our valuable customers. I also need to express my eternal gratitude to all the CTOs and leaders who have been kind enough to share their experiences over these years. Shoutout to Dani Lopez, Peter Silberman, Alex Plugaru, Kwindla Hultman Kramer, João Batalha, Karri Saarinen, Miguel Martinez Triviño, Javi Santana, Matias Woloski, Tobias Balling, Jason Warner, and Will Larson. Our investors and early believers Jason Lemkin, Anu Hariharan, Mark Fiorentino, Mark Goldberg, Andrew Maguire, Gustaf Alströmer, Sofia Dolfie, and Nico Wittenborn. I want to convey my deep gratitude to my amazing wife, Marina, who has been my unwavering source of inspiration and support from the very beginning, and for blessing us with our two precious daughters. I cannot close this post without thanking my mom, who made countless sacrifices to mold me into the person I am today. I promise you will look down on us with pride the day we ring the bell in New York. I love you dearly.

6 months ago 68 votes
Full Circle

I’m back in Spain for my brother’s wedding. I rarely visit during the summer. The heat in my hometown is brutal, around 40 degrees Celsius (over 100 Fahrenheit for my imperial friends). Most people escape to the coast, just like my family did when I was a kid. I haven’t been here in years. As I drive along the coast, I find myself reflecting on a tweet about money and happiness, a vivid memory pulls me back in time. It’s August 22nd, 2007. The iPhone, the first real smartphone, has just been announced. It’s so cool, but of course I cannot afford it. It’s not even going to be released in Spain. I’ve just gotten my driver’s license, and I’m about to dive into my third year of Computer Science. I set up my clunky TomTom navigator knockoff, and hit the road. I’m on my way to meet Marina for our first real date. She’s cool, pretty, and kind. She likes the same music as I do, and even has distant relatives in California, the place we jokingly plan to visit someday (if I ever get enough cash). I’m listening to a pirated Blink-182’s self-titled CD. Pop-punk is pretty niche in the south of Spain, and it’s dying. Blink-182 has split up, and I missed my window to see my favorite band live. The Atlantic Ocean is as flat as a lake. This corner of Huelva’s coast is sheltered from any real waves, a stark contrast to the world-class surf breaks I drool over in magazines. And suddenly, reality hits: my childhood dreams of building a tech company in Silicon Valley, while vacationing in Southern California feel impossibly far. Even getting through my degree feels like a pipe dream. School isn’t fun anymore. It’s grueling, especially the parts I thought I’d enjoy, like algorithms and Data Structures. I might never become a Software Engineer. I feel stuck, trapped by my lack of direction. I am seriously considering quitting. But no degree means no job in the US, and good tech gigs are rare here in Spain. The only cool company is Tuenti, a new startup that is cloning Facebook. I’m nowhere near smart enough to land a job there though. Flash forward to today, 17 years later. It’s almost laughable to think about how hopeless things once seemed. Even now, it doesn’t feel like I’ve “made it.” The path and the results look nothing like what teenage me envisioned, but somehow, I’m realizing I’ve kind of checked off every box. I married Marina, and we live in Southern California with our two beautiful identical kids. We’ve become American citizens, and I’ve lived in the Golden State for nearly a third of my life. I’ve worked as a Software Engineer at a Silicon Valley startup, learned from the best, and found the best co-founder I could ask for. We launched our own company. Smartphones? They’re in everyone’s pockets now. Our product is in a third of all new apps shipped in the US. We’ve helped developers reach millionaire status, and we’ve made more money than I ever thought possible. But I’ve learned that a lot of money is a relative term. Somehow, I managed to hire insanely talented engineers—a bunch of them, ironically, from Tuenti. Blink-182 is back together, and I’ve been fortunate enough to see them live five times. I’ve even bumped into Tom Delonge after surfing world-class waves a few times. I’m literally just realizing how surreal all of this is. I tend to get caught up in the chaos of what’s next—the next big fire, the next goal—but sometimes you’ve got to stop, be present, and reflect on how far you’ve come. It wasn’t easy. It wasn’t without loss, sacrifice, and a fair share of doubts. Am I truly happy? Maybe not in a perfect, all-the-time kind of way. There are external things humans cannot control. But when I look at my life, I realize there’s no real reason not to be. The journey has been was worth it so far: the ups, the downs, the unexpected turns. So, here’s to your journey, whatever it looks like. Keep going, keep dreaming. It might not turn out the way you envisioned it, but it’s only impossible if you quit.

10 months ago 76 votes
From J1 visa to Blue Passport: A startup founder's immigration journey

I am drafting this post at 35,000 feet flying back from Japan. I’ve entered the US about 30 times, but this will be the first time I’ll be using my shiny blue passport. No anxiety about aggressive questions, secondary inspection, or the possibility of deportation. A couple of days ago, my wife had her naturalization ceremony, and with her, our whole family is now American. This post is a reflecting on our 11-year immigration journey. My American Dream My story with technology started at 8, with my first computer. I fell in love and decided that one day I would start a computer business. And of course, it would have to be in Silicon Valley, the epicenter of innovation. I grew up influenced by the iconic Californian lifestyle of the 90s, from Tony Hawk to bands like Blink-182 and The Offspring, which only fueled my desire to call the West Coast home. As I finished my Computer Science studies, the reality of achieving my American dream seemed increasingly distant. My enthusiasm for the Californian way of life hadn’t waned — I had started surfing and I was even playing in a pop-punk band. But the immigration complexities were too daunting. It looked impossible. I decided to study a Master’s Degree in the UK and reinforce my English. Right before completing my degree in England, I was offered a six-month internship at a startup in San Francisco. Financially, it was not the smartest decision — I would barely be able to afford rent, despite having better paid options in Europe. However, experiencing Silicon Valley was a lifelong dream. It wasn’t an obvious choice. But I ultimately packed my suitcase, left behind my family, girlfriend and friends, and relocated across the world. Landing in California My journey in the U.S. began with a J1 visa, intended for interns and relatively simple to secure with an employer’s backing. It was a suitable fit for my six-month plan, extendable to a full year, without any ambition for a longer stay. Yet, I worked extremely hard, and the situation changed when I was introduced to the possibility of obtaining an H-1B visa. Unlike the J1, the H-1B visa demands wage parity with U.S. citizens, allows for a stay of up to six years, and paves the way for permanent residency. Most importantly, it meant I could start laying down roots in the US, such as building my credit score, buying a car, or negotiating a long-term lease. First Immigration Problems My living situation dramatically improved when I traded my small, rat-filled room for a two-bedroom apartment in the Outer Sunset, sharing the space with a friend. I got a sizeable salary bump. However, the happiness was short-lived. H-1B visa applications had exceeded available spots for the first time in years, introducing a lottery. My coding skills, education, and value to my employer wouldn’t factor into this gamble. Anxiety mounted for a very long month, as friends celebrated their visa wins. I was left in the dark, bracing for bad news. Against the odds, relief came just a day after a disheartening talk with my immigration lawyer, granting me my first taste of luck. Reuniting with my girlfriend Sick. My H-1B visa approval meant I could make the US my home for six additional years, longer than Marina and I had been dating. As she was about to end her studies, we strategized on ways to reunite in the US. Marrying earlier was an option, but with H-1B restrictions preventing spouses from working, we looked for alternatives. The easiest path forward involved securing a student visa, leading to an 12-month work permit, followed by an H-1B visa application. In the competitive climate of 2015, her work visa acceptance felt nothing short of miraculous, becoming our story’s second lucky strike. Permanent Residency While six years might seem long, they pass quickly when you are busy and having fun. Halfway through, it became clear we needed to strategize for the future when it was time to renew my visa. My employer agreed to initiate the Green Card application, a lengthy and costly process requiring proof that I was indispensable for the company. Despite the complexities, our attorney believed the case would progress smoothly, estimating an 18-month completion time. Surprisingly, the initial phase went way faster than anticipated, prompting our attorney to suggest an immediate wedding for Marina and me, a necessary step to include her in the Green Card application. We quickly scheduled our wedding at the Spanish Consulate in San Francisco, departing from our original plan for a ceremony in Spain. More problems A year and a half in, expecting our Green Cards, we faced an unexpected challenge: our marriage, officiated at a consulate, was not recognized by US Immigration, compelling us to marry again, pay the associated fees again, and start the process over. This development was extremely frustrating, specially as my friend Jacob and I were contemplating founding a company, and the absence of a Green Card meant remaining as an employee. To rectify the situation, we promptly got remarried at San Mateo City Hall, choosing it for its rapid scheduling. Two months later we held another ceremony in Spain with our family and friends (our third marriage overall). The delay in our reapplication, exacerbated by the recent election of Trump and a subsequent slowdown in immigration services, led us into a stressful period of uncertainty. Our inability to make definite plans, from household purchases to housing arrangements left us anxiously awaiting any news on our application. As we navigated this uncertainty, the possibility of dedicating myself fully to our startup (later named RevenueCat) became increasingly dim. Going All In A pivotal moment came when our now startup, RevenueCat, was accepted into Y Combinator, requiring my full-time commitment. A big challenge due to my pending Green Card application. My immigration status became the biggest risk to our startup, even before launching. In searching for a solution, we identified a potential hack: an immigration loophole that allowed for employment changes under specific circumstances. It wasn’t risk free. There were no guarantees, and it involved giving up my H-1B status and the ability to travel. Should my Green Card application face rejection for any reason, I would instantly become an illegal immigrant. Deciding to take the gamble, we prepared the necessary documentation. To support our case, Jacob, my co-founder, had to write a letter stating that although my compensation was on the lower side, his, in the CEO role, was even lower. We also had to declare our company’s annual earnings ($0 at that time), and I suggested Jacob to specify it as less than $1 million. You should never lie to Homeland Security 😅. This leap of faith paid off; eight months later, we received our Green Card interview, where the immigration officer, making fun of our unique circumstances, granted approval on the spot. The Decision to Become American Those with an employment-based Green Card need a five-year stay in the US, compliant with all laws and tax requirements, to qualify for citizenship. While obtaining citizenship is not mandatory, and one might choose to stay a permanent resident indefinitely, citizenship confers full rights and responsibilities. Among these, the requirement to pay federal taxes forever, a big concern for many. If RevenueCat succeeds as we hope, I’m looking at significant tax payments, even if we end up moving back to Europe. The decision to embrace US citizenship came down to a simple reason: we can. We recognize the privilege of this choice, acknowledging the series of fortunate events that brought us here, aware that our journey could have taken decades had we originated from countries like India or China. Our twin daughters are blessed with dual citizenship, offering them a breadth of choices for their future. The opportunities the US has presented to our family are beyond what we once could dream. By becoming citizens, we gain a voice to influence immigration policies positively instead of blocking progress. My tax contributions have already reached the seven-figure mark. The continuation of our tax obligation is a small price to pay. And after all, there are some advantageous double taxation agreements 😉. Special thanks to my family for always supporting me and pushing me to live my dream in San Francisco. To Marina, my now wife, for joining me in this crazy adventure across the globe. To everyone at StepOne for running an amazing internship program. To Jesse, for believing in my potential and tackling the immigration challenges with me. And to my co-founder Jacob, for pushing me to take a leap of faith with my immigration status.

a year ago 57 votes
My role as a founder CTO: Year Six

Another year as a founder CTO, and let me tell you, it’s been one for the books. I can’t remember a time in my life that was more demanding and emotionally draining. Those early years were filled with hard work, but we were also full of energy, ambition to build, and the sense that we had absolutely nothing to lose. As my dear friend Jacob used to quip, “Worst case scenario nobody dies”. Yet, everything takes on a new perspective when you’re responsible for the monetization infrastructure of over 30,000 apps and ensuring the livelihoods of not only your team but also your own family. While I would typically begin this series of posts with an array of metrics, this year has been a whirlwind of events that has taken precedence over mere numbers. So, dear friend, take a seat and allow me to share with you a deeply personal and epic firefighting tale and the battle to restore the flame of RevenueCat’s core values. The Year of Shipping We embarked on Q1 with big intentions and an ambitious roadmap. With seven new engineering teams, my role was to lead the one responsible for serving our larger customers. My days consisted of collaborating with our (still tiny) sales team, while simultaneously overseeing our support function. While I continued to provide technical guidance to several projects, I made the conscious decision to delegate the bulk of product development to the other teams. In hindsight, this decision proved to be a mistake. The teams were still finding their footing, with a mix of new hires, and there was a disconnect between engineering and product that needed addressing — a topic we’ll get into later. Nevertheless, the team I led was firing on all cylinders, delivering enterprise-level features at a hasty pace. From implementing single-sign-on in a matter of days, to tackling gnarly bugs, this team’s momentum was inspiring. I hoped that this stride would serve as a catalyst, motivating the rest of the teams to achieve a similar velocity. On the home front, my wife’s parental leave came to an end in January, marking the start of a new era where my hours were no longer flexible. Together, we embarked on the demanding task of caring for our twin babies. Little did I know how much the lack of sleep would impact me. Fatigue began to set in and my usual escape valves, such as surfing, turned into distant memories. Caffeine became my closest friend, helping me stay awake as I navigated the demands of work and family. I would spend my entire weekends lying in bed, trying to recover. I wrongly assumed it would be a short phase. Troubles never come alone On the fateful morning of March 9th, while I was soothing one of our crying babies at 4:30 AM, my WhatsApp began buzzing with messages from concerned fellow founders. Rumors were swirling that Silicon Valley Bank, the custodian of most VC-backed startup funds, was about to collapse. Within hours these whispers escalated into a full-blown bank run and we found ourselves unable to access our funds. Without going into the nitty-gritty details, it was an intensely stressful weekend. We were on the verge of a payroll crisis and we didn’t even have a functioning bank to transact with. However, we couldn’t afford to let this situation be what killed RevenueCat. We worked to expedite the opening of new bank accounts and explored multiple liquidity alternatives. Fortunately, we were privileged enough to entertain multiple options. One investor came to help, wiring funds from their personal account, and some of our loyal customers offered to pay in advance. Just as hope seemed to dwindle, the FDIC announced that they would guarantee all deposits on that Sunday afternoon. Crisis averted. Emotionally drained from the weekend’s trauma, we received another unexpected blow just a few days later: Y Combinator was discontinuing its Continuity Fund. This created a dilemma regarding the fate of their board seat, something we would have never anticipated. Just weeks prior, we had suffered a severe outage caused by our cloud provider. We tried to console ourselves, thinking, “At least all these problems are internal; we’re not dealing with downtime”. A Crisis of Reliability Our break was short-lived. Less than a week after the Silicon Valley Bank fiasco we experienced yet another major outage, and this time, it was self-inflicted. When you’re handling hundreds of thousands of API requests per second, even a few seconds of downtime can set off a cascade of alarms and potential sales losses. Our customers were understandably frustrated, and they didn’t hesitate to make their displeasure known through every available channel. It was heartbreaking to witness and I felt a deep sense of personal failure. This marked the third user-facing issue in a matter of months. It wasn’t something to sweep under the rug: our reputation was on the line and it was time to act. By this point, I was utterly sleep-deprived and my Apple Watch had begun sending me concerning health notifications. The day after we resolved the outage, my co-founder Jacob and I came up with an action plan. It was officially wartime. We needed to act decisively. We publicly unveiled our plan: to develop a robust fallback system independent of our current infrastructure. And we promised to deliver it within a week. Simultaneously, Jacob and I embarked on an apology tour, reaching out personally to some of our most worried customers. This was a painful but humbling experience for us, but it proved beneficial on multiple fronts. It allowed us to reiterate our commitment to becoming the best in-app subscription infrastructure provider in the world while gaining invaluable insights into our customers’ pain points. RC Fortress I rallied a small crew of engineers from different teams to build the first version of what we called “RevenueCat Fortress”. This component was designed to make sure end-customers could purchase seamlessly, even when our main servers were unavailable. It was a crazy week because we set ourselves a tight deadline but it helped boost our spirits and proved we could deliver software fast. The initial version of RevenueCat Fortress was quite simple – it operated behind the scenes on the server. But we didn’t stop there. We made it even better in the next iterations by adding SDK improvements such as offline entitlements. When it finally rolled out, it did so with flying colors. We even got to put it to the test during a major Apple outage and it saved the day for RevenueCat customers, making them immune to Apple’s downtime. Turning things around Looking back, the birth of RC Fortress marked the start of a shift in our culture. It got us back to the basics of reliability, fast delivery, and customer obsession. We couldn’t afford to spend months on extensive, untested projects. We had to rapidly build the features our customers valued most and iterate from there. We also realized that keeping things rock-solid wasn’t just the infrastructure team’s job; it was a global effort. Around the same time, we faced a couple of setbacks when we parted ways with two executives – the VP of People and the VP of Engineering. We tried to find a new VP of Engineering but couldn’t find a match that really excited us. So, the board agreed it would be best if I took the reins of the entire engineering organization again. Those days gave me a chance to get closer to the product teams again. Here’s where I spent most of my energy: Performance: I clarified expectations, provided feedback, and coached managers on performance management. Hiring: We tweaked our hiring process and re-calibrated interviewers’ expectations. Reliability and Quality: We were pretty good at doing post-mortems after issues but we had too many of them. They lacked detail and they weren’t followed through with action items. We needed a little bit of a cultural reset. We introduced dedicated incident Slack channels and clearly defined roles. Customer Obsession: Taking over the support team was eye-opening. It gave me a direct line to our product’s weak spots and what confused our customers. We started categorizing support tickets and sending them straight to the right product teams for triaging. Project Management: We focused on breaking projects into smaller chunks to deliver faster, instead of getting lost in never-ending projects. Education and Best Practices: I spent time educating other departments, especially post-sales teams, to avoid recurring mistakes that were slowing down our engineering progress. On top of all that, I reconnected with our customers more than ever. I hopped on planes to visit them at their offices and even worked our booth at a few conferences. It was a refreshing change to chat with users face-to-face, and hearing their unique challenges in person after a long time. Life is what happens when you’re busy running your startup We were fortunate enough to fly in my mother-in-law to assist with our babies, which made my travel plans possible. I finally felt more rested and even managed to squeeze in a few surfing sessions. Things were looking up both personally and professionally. I was eagerly anticipating our annual company-wide offsite, especially since I had missed most of the previous one due to my wife’s high-risk pregnancy. This time around I was geared up to address the entire engineering team, sharing the exciting changes and boosting morale. We were about to start winning again. But then, the day before my flight, I received a call from my father back in Spain. My mother had been rushed to the hospital, and she had been diagnosed with an extremely aggressive form of leukemia. Time seemed to stand still. I boarded the flight as planned, chugged two Red Bulls, and delivered that motivational talk to the whole team, while my mom was in a hospital bed thousands of miles away. This was hands down the toughest thing I’ve ever done as a startup founder. I left the offsite early and headed back to my hometown. Over the following weeks, I traveled back and forth around the globe, coordinating with my family. Sadly, my mother never left the hospital, she passed away merely a few weeks after the diagnosis. These are the things that always lurk in the back of your mind when you’re living 10,000 miles away from home, but you never truly believe they’ll happen. Until they do, and they shatter you. Keep on pushing The days that followed were far from easy. We were dealing with the launch of our biggest customer’s app, something we’d been preparing for a long time. The scale was enormous and the hard work of our team truly paid off. Our systems ran incredibly smoothly. It was a monumental victory, especially after the rocky start to the year. But, mentally, I wasn’t prepared to savor the moment. Yet, the energy post-offsite was infectious. People genuinely enjoyed meeting each other in real life and were fired up to start shipping. We couldn’t let this opportunity slip through our fingers. I paid homage to my mom’s teachings by continuing to press forward. RC Paywalls One of the major product ideas that had always been on our team’s wishlist was paywalls. We hadn’t tackled it because it seemed daunting and we lacked a product team with all the necessary skills. We couldn’t even estimate how long it would take to build. But, fueled by the success of RC Fortress, we decided to take a shot at it. We assembled a small team with members borrowed from different corners of the company. We didn’t mess with any reporting structures but appointed a leader. We went back to our roots, working in a hackathon-style frenzy for a couple of weeks to build a prototype. Just like in the good old days. And, boy, did the team rise to the occasion. We gave them some extra time and, in the end, they delivered one of our biggest product wins of the year. Shipping paywalls felt like a breath of fresh air and a clear sign that we still had our mojo. We were still capable of shipping software at lightning speed and keeping our customers excited. Based on all these lessons, the Head of Product and I started cooking up a brand new way of building products at RevenueCat. Engineering/Product/Design changes The complete process overhaul would warrant a couple of blog posts, but let me highlight the key changes to our workflow: We’ve reviewed all existing teams to determine whether they should continue as is, be replaced, or undergo changes in their mission or structure. We’ve rebalanced and clarified the responsibilities across Product, Engineering, and Design: Engineering takes the lead on feasibility, delivery, and developer experience, managing Linear, and overseeing technical architecture (and debt) roadmaps. Product is dedicated to customer value, business viability, and collaboration with sales and marketing teams. They also provide support to engineering in refining project scopes. Design is responsible for usability. We’ve formalized the role of Tech Lead, assigning them as the Directly Responsible Individuals for specific projects. They’re accountable for project success, with full backing from their Engineering Manager. It’s optional and project-dependent and doesn’t entail a title or salary change. We’ve acknowledged the need for engineering-driven initiatives, where PM and Design are involved on an as-needed basis. For projects with dependencies with other teams, we’ll designate a team member from the collaborating team as a formal interface. Our existing setup of stable product teams will remain the norm for most of our work. Temporary project teams will be established only when there’s a strong need for cross-team collaboration over a limited period. We’ll conduct monthly roadmap and shipping reviews with the founders and Head of Product. These reviews will provide insights into what we’re building, offer feedback opportunities, and help identify cross-functional dependencies and misalignments. 2024: The year of shipping + selling Collaborating with the Product team showcased the immense benefits of working closely together. Traditionally, Product reported directly to our CEO, which introduced unnecessary layers of indirection. In light of this and our recent addition of a VP of Sales, we decided it was time for a reorganization. Currently, Jacob (co-founder, and CEO), is overseeing Go to Market, People, and Operations, while I’m responsible for Engineering, Product, and Customer Engineering. We’ve brought in a VP of Customer Engineering, who reports to me and is in charge of Support and Technical Account Management. With our current headcount at 73 employees, my organization consists of 48 team members. Our executive team developed the most comprehensive planning effort to date. Our goal is to accelerate growth, focusing on sales and product delivery. We will avoid distractions by being extremely strategic at hiring. The past quarter showcased the strength of our engineering and product teams. New team members have been onboarded successfully, contributing meaningfully, and our management structure is finally robust. It took a bit of time for the “year of shipping” to fully materialize, nearly a year later, but customers have taken notice and we’re capitalizing on this momentum. On the enterprise sales front, I’m extremely bullish. We’ve secured the biggest deals in the company’s history. RevenueCat has evolved beyond being a product just for indie developers. However, we acknowledge the need to continue closing the product-market fit gap for enterprise clients. We’ve gained valuable insights into enterprise needs, and we’ll keep developing new products and features tailored to them. Indie developers will also take advantage of them to make more money. In 2024, the collaboration between our go-to-market and engineering teams will be critical. Highlights This year, as you’ve probably noticed, was a tough one. However, besides the challenges, there were several remarkable achievements to celebrate: We truly shipped. Our second hackathon, spanning an entire week, was an epic success. Many of the projects launched immediately, directly benefiting our customers. We had the privilege of collaborating with prominent brands and companies, including none other than Arnold Schwarzenegger himself. We created an astonishing amount of high-quality content. Our SubClub podcast outperformed all my expectations. During the chaos of the bank run, we discovered that one of our idols was not only a RevenueCat customer but also a devoted fan. We experimented with fresh team topologies and processes, and they turned out to be successful. We fine-tuned our vision for building a winning team, offering improved feedback, clearer expectations, and timely performance management. Jacob and I are no longer the sole authorities on Apple and Google subscriptions within the company. We successfully recruited seasoned executives to join our team. Our core infrastructure team accomplished monumental feats. We now support over 1 billion API requests daily, transitioned to our own data platform, and developed our own memcached client. All while maintaining flat costs despite the increase in load. Learnings It’s impossible to achieve peak performance without attention to health, exercise, and sleep. I’m no longer in my twenties. Complexity is the root of all evil. Startups and software are inherently complex, so avoid introducing unnecessary complexity. Begin by building the simplest feature or process, debug it, and then iterate as needed. Starting a business is tough, but launching a remote startup is an even greater challenge. Scaling a remote startup while parenting two under two is a herculean effort. Building a brand takes years but its reputation can be destroyed in an instant. Protect the integrity of your brand at all costs. Every new team member should add value, and especially so in a startup. Some provide immense leverage, while others become bottlenecks. The trickiest are those in the middle, who often end up becoming bottlenecks. Founders usually spot this within the first few weeks. Letting someone go is a taxing task. Even when managers believe it’s the right thing to do, it often requires a significant amount of support and guidance. Transparency in times of crisis pays dividends. Employees want to be treated like adults, and it builds trust. The same applies externally. Early worries often become baseless. By the time they become actual problems, your company might have died, you might have gained experience, or you might have hired the right talent to tackle them. SOC 2 auditors may request unconventional supporting evidence, such as employee performance reviews. Developers love socks. I’m so fortunate to have the world’s best co-founder. At this stage, my role is much more aligned with that of a founder than a traditional CTO or VP of Engineering. I continue to address issues as if they were technical problems, but my responsibilities extend well beyond the technology area. Life keeps moving forward, with its share of highs and lows. Life is too short, so you have to ensure the journey remains fun. For me, that means working with people who inspire me, and serving customers I genuinely care about. I really hope you enjoyed reading this post. I’m aware it’s a lot longer than my previous ones, but there were so many stories to share. As always, my intention was to share it with complete honesty and transparency, avoiding the hype that often surrounds startups. If you are facing similar challenges and want to connect and share experiences, please do not hesitate to reach out on Twitter or shoot me an email! Special thanks to my co-founder Jacob, the whole RevenueCat team, and our valuable customers. I also need to express my eternal gratitude to all the CTOs and leaders who have been kind enough to share their experiences over these years. Shoutout to Dani Lopez, Peter Silberman, Alex Plugaru, Kwindla Hultman Kramer, João Batalha, Karri Saarinen, Miguel Martinez Triviño, Sam Lown, Javi Santana, Pau Ramón, Javier Maestro, Matias Woloski, Tobias Balling, Jason Warner, and Will Larson. Our investors and early believers Jason Lemkin, Anu Hariharan, Mark Fiorentino, Mark Goldberg, Andrew Maguire, Gustaf Alströmer, and Nico Wittenborn. I want to convey my deep gratitude to my amazing wife, Marina, who has been my unwavering source of inspiration and support from the very beginning, and for blessing us with our two precious daughters. I cannot close this post without thanking my mom, who made countless sacrifices to mold me into the person I am today. I promise you will look down on us with pride the day we ring the bell in New York. I love you dearly.

a year ago 46 votes

More in programming

My first year since coming back to Linux

<![CDATA[It has been a year since I set up my System76 Merkaat with Linux Mint. In July of 2024 I migrated from ChromeOS and the Merkaat has been my daily driver on the desktop. A year later I have nothing major to report, which is the point. Despite the occasional unplanned reinstallation I have been enjoying the stability of Linux and just using the PC. This stability finally enabled me to burn bridges with mainstream operating systems and fully embrace Linux and open systems. I'm ready to handle the worst and get back to work. Just a few years ago the frustration of troubleshooting a broken system would have made me seriously consider the switch to a proprietary solution. But a year of regular use, with an ordinary mix of quiet moments and glitches, gave me the confidence to stop worrying and learn to love Linux. linux a href="https://remark.as/p/journal.paoloamoroso.com/my-first-year-since-coming-back-to-linux"Discuss.../a Email | Reply @amoroso@oldbytes.space !--emailsub--]]>

17 hours ago 3 votes
Overanalyzing a minor quirk of Espressif’s reset circuit

The mystery In the previous article, I briefly mentioned a slight difference between the ESP-Prog and the reproduced circuit, when it comes to EN: Focusing on EN, it looks like the voltage level goes back to 3.3V much faster on the ESP-Prog than on the breadboard circuit. The grid is horizontally spaced at 2ms, so … Continue reading Overanalyzing a minor quirk of Espressif’s reset circuit → The post Overanalyzing a minor quirk of Espressif’s reset circuit appeared first on Quentin Santos.

18 hours ago 2 votes
What can agents actually do?

There’s a lot of excitement about what AI (specifically the latest wave of LLM-anchored AI) can do, and how AI-first companies are different from the prior generations of companies. There are a lot of important and real opportunities at hand, but I find that many of these conversations occur at such an abstract altitude that they’re a bit too abstract. Sort of like saying that your company could be much better if you merely adopted software. That’s certainly true, but it’s not a particularly helpful claim. This post is an attempt to concisely summarize how AI agents work, apply that summary to a handful of real-world use cases for AI, and make the case that the potential of AI agents is equivalent to the potential of this generation of AI. By the end of this writeup, my hope is that you’ll be well-armed to have a concrete discussion about how LLMs and agents could change the shape of your company. How do agents work? At its core, using an LLM is an API call that includes a prompt. For example, you might call Anthropic’s /v1/message with a prompt: How should I adopt LLMs in my company? That prompt is used to fill the LLM’s context window, which conditions the model to generate certain kinds of responses. This is the first important thing that agents can do: use an LLM to evaluate a context window and get a result. Prompt engineering, or context engineering as it’s being called now, is deciding what to put into the context window to best generate the responses you’re looking for. For example, In-Context Learning (ICL) is one form of context engineering, where you supply a bunch of similar examples before asking a question. If I want to determine if a transaction is fraudulent, then I might supply a bunch of prior transactions and whether they were, or were not, fraudulent as ICL examples. Those examples make generating the correct answer more likely. However, composing the perfect context window is very time intensive, benefiting from techniques like metaprompting to improve your context. Indeed, the human (or automation) creating the initial context might not know enough to do a good job of providing relevant context. For example, if you prompt, Who is going to become the next mayor of New York City?, then you are unsuited to include the answer to that question in your prompt. To do that, you would need to already know the answer, which is why you’re asking the question to begin with! This is where we see model chat experiences from OpenAI and Anthropic use web search to pull in context that you likely don’t have. If you ask a question about the new mayor of New York, they use a tool to retrieve web search results, then add the content of those searches to your context window. This is the second important thing that agents can do: use an LLM to suggest tools relevant to the context window, then enrich the context window with the tool’s response. However, it’s important to clarify how “tool usage” actually works. An LLM does not actually call a tool. (You can skim OpenAI’s function calling documentation if you want to see a specific real-world example of this.) Instead there is a five-step process to calling tools that can be a bit counter-intuitive: The program designer that calls the LLM API must also define a set of tools that the LLM is allowed to suggest using. Every API call to the LLM includes that defined set of tools as options that the LLM is allowed to recommend The response from the API call with defined functions is either: Generated text as any other call to an LLM might provide A recommendation to call a specific tool with a specific set of parameters, e.g. an LLM that knows about a get_weather tool, when prompted about the weather in Paris, might return this response: [{ "type": "function_call", "name": "get_weather", "arguments": "{\"location\":\"Paris, France\"}" }] The program that calls the LLM API then decides whether and how to honor that requested tool use. The program might decide to reject the requested tool because it’s been used too frequently recently (e.g. rate limiting), it might check if the associated user has permission to use the tool (e.g. maybe it’s a premium only tool), it might check if the parameters match the user’s role-based permissions as well (e.g. the user can check weather, but only admin users are allowed to check weather in France). If the program does decide to call the tool, it invokes the tool, then calls the LLM API with the output of the tool appended to the prior call’s context window. The important thing about this loop is that the LLM itself can still only do one interesting thing: taking a context window and returning generated text. It is the broader program, which we can start to call an agent at this point, that calls tools and sends the tools’ output to the LLM to generate more context. What’s magical is that LLMs plus tools start to really improve how you can generate context windows. Instead of having to have a very well-defined initial context window, you can use tools to inject relevant context to improve the initial context. This brings us to the third important thing that agents can do: they manage flow control for tool usage. Let’s think about three different scenarios: Flow control via rules has concrete rules about how tools can be used. Some examples: it might only allow a given tool to be used once in a given workflow (or a usage limit of a tool for each user, etc) it might require that a human-in-the-loop approves parameters over a certain value (e.g. refunds more than $100 require human approval) it might run a generated Python program and return the output to analyze a dataset (or provide error messages if it fails) apply a permission system to tool use, restricting who can use which tools and which parameters a given user is able to use (e.g. you can only retrieve your own personal data) a tool to escalate to a human representative can only be called after five back and forths with the LLM agent Flow control via statistics can use statistics to identify and act on abnormal behavior: if the size of a refund is higher than 99% of other refunds for the order size, you might want to escalate to a human if a user has used a tool more than 99% of other users, then you might want to reject usage for the rest of the day it might escalate to a human representative if tool parameters are more similar to prior parameters that required escalation to a human agent LLMs themselves absolutely cannot be trusted. Anytime you rely on an LLM to enforce something important, you will fail. Using agents to manage flow control is the mechanism that makes it possible to build safe, reliable systems with LLMs. Whenever you find yourself dealing with an unreliable LLM-based system, you can always find a way to shift the complexity to a tool to avoid that issue. As an example, if you want to do algebra with an LLM, the solution is not asking the LLM to directly perform algebra, but instead providing a tool capable of algebra to the LLM, and then relying on the LLM to call that tool with the proper parameters. At this point, there is one final important thing that agents do: they are software programs. This means they can do anything software can do to build better context windows to pass on to LLMs for generation. This is an infinite category of tasks, but generally these include: Building general context to add to context window, sometimes thought of as maintaining memory Initiating a workflow based on an incoming ticket in a ticket tracker, customer support system, etc Periodically initiating workflows at a certain time, such as hourly review of incoming tickets Alright, we’ve now summarized what AI agents can do down to four general capabilities. Recapping a bit, those capabilities are: Use an LLM to evaluate a context window and get a result Use an LLM to suggest tools relevant to the context window, then enrich the context window with the tool’s response Manage flow control for tool usage via rules or statistical analysis Agents are software programs, and can do anything other software programs do Armed with these four capabilities, we’ll be able to think about the ways we can, and cannot, apply AI agents to a number of opportunities. Use Case 1: Customer Support Agent One of the first scenarios that people often talk about deploying AI agents is customer support, so let’s start there. A typical customer support process will have multiple tiers of agents who handle increasingly complex customer problems. So let’s set a goal of taking over the easiest tier first, with the goal of moving up tiers over time as we show impact. Our approach might be: Allow tickets (or support chats) to flow into an AI agent Provide a variety of tools to the agent to support: Retrieving information about the user: recent customer support tickets, account history, account state, and so on Escalating to next tier of customer support Refund a purchase (almost certainly implemented as “refund purchase” referencing a specific purchase by the user, rather than “refund amount” to prevent scenarios where the agent can be fooled into refunding too much) Closing the user account on request Include customer support guidelines in the context window, describe customer problems, map those problems to specific tools that should be used to solve the problems Flow control rules that ensure all calls escalate to a human if not resolved within a certain time period, number of back-and-forth exchanges, if they run into an error in the agent, and so on. These rules should be both rules-based and statistics-based, ensuring that gaps in your rules are neither exploitable nor create a terrible customer experience Review agent-customer interactions for quality control, making improvements to the support guidelines provided to AI agents. Initially you would want to review every interaction, then move to interactions that lead to unusual outcomes (e.g. escalations to human) and some degree of random sampling Review hourly, then daily, and then weekly metrics of agent performance Based on your learnings from the metric reviews, you should set baselines for alerts which require more immediate response. For example, if a new topic comes up frequently, it probably means a serious regression in your product or process, and it requires immediate review rather than periodical review. Note that even when you’ve moved “Customer Support to AI agents”, you still have: a tier of human agents dealing with the most complex calls humans reviewing the periodic performance statistics humans performing quality control on AI agent-customer interactions You absolutely can replace each of those downstream steps (reviewing performance statistics, etc) with its own AI agent, but doing that requires going through the development of an AI product for each of those flows. There is a recursive process here, where over time you can eliminate many human components of your business, in exchange for increased fragility as you have more tiers of complexity. The most interesting part of complex systems isn’t how they work, it’s how they fail, and agent-driven systems will fail occasionally, as all systems do, very much including human-driven ones. Applied with care, the above series of actions will work successfully. However, it’s important to recognize that this is building an entire software pipeline, and then learning to operate that software pipeline in production. These are both very doable things, but they are meaningful work, turning customer support leadership into product managers and requiring an engineering team building and operating the customer support agent. Use Case 2: Triaging incoming bug reports When an incident is raised within your company, or when you receive a bug report, the first problem of the day is determining how severe the issue might be. If it’s potentially quite severe, then you want on-call engineers immediately investigating; if it’s certainly not severe, then you want to triage it in a less urgent process of some sort. It’s interesting to think about how an AI agent might support this triaging workflow. The process might work as follows: Pipe all created incidents and all created tickets to this agent for review. Expose these tools to the agent: Open an incident Retrieve current incidents Retrieve recently created tickets Retrieve production metrics Retrieve deployment logs Retrieve feature flag change logs Toggle known-safe feature flags Propose merging an incident with another for human approval Propose merging a ticket with another ticket for human approval Redundant LLM providers for critical workflows. If the LLM provider’s API is unavailable, retry three times over ten seconds, then resort to using a second model provider (e.g. Anthropic first, if unavailable try OpenAI), and then finally create an incident that the triaging mechanism is unavailable. For critical workflows, we can’t simply assume the APIs will be available, because in practice all major providers seem to have monthly availability issues. Merge duplicates. When a ticket comes in, first check ongoing incidents and recently created tickets for potential duplicates. If there is a probable duplicate, suggest merging the ticket or incident with the existing issue and exit the workflow. Assess impact. If production statistics are severely impacted, or if there is a new kind of error in production, then this is likely an issue that merits quick human review. If it’s high priority, open an incident. If it’s low priority, create a ticket. Propose cause. Now that the incident has been sized, switch to analyzing the potential causes of the incident. Look at the code commits in recent deploys and suggest potential issues that might have caused the current error. In some cases this will be obvious (e.g. spiking errors with a traceback of a line of code that changed recently), and in other cases it will only be proximity in time. Apply known-safe feature flags. Establish an allow list of known safe feature flags that the system is allowed to activate itself. For example, if there are expensive features that are safe to disable, it could be allowed to disable them, e.g. restricting paginating through deeper search results when under load might be a reasonable tradeoff between stability and user experience. Defer to humans. At this point, rely on humans to drive incident, or ticket, remediation to completion. Draft initial incident report. If an incident was opened, the agent should draft an initial incident report including the timeline, related changes, and the human activities taken over the course of the incident. This report should then be finalized by the human involved in the incident. Run incident review. Your existing incident review process should take the incident review and determine how to modify your systems, including the triaging agent, to increase reliability over time. Safeguard to reenable feature flags. Since we now have an agent disabling feature flags, we also need to add a periodic check (agent-driven or otherwise) to reenable the “known safe” feature flags if there isn’t an ongoing incident to avoid accidentally disabling them for long periods of time. This is another AI agent that will absolutely work as long as you treat it as a software product. In this case, engineering is likely the product owner, but it will still require thoughtful iteration to improve its behavior over time. Some of the ongoing validation to make this flow work includes: The role of humans in incident response and review will remain significant, merely aided by this agent. This is especially true in the review process, where an agent cannot solve the review process because it’s about actively learning what to change based on the incident. You can make a reasonable argument that an agent could decide what to change and then hand that specification off to another agent to implement it. Even today, you can easily imagine low risk changes (e.g. a copy change) being automatically added to a ticket for human approval. Doing this for more complex, or riskier changes, is possible but requires an extraordinary degree of care and nuance: it is the polar opposite of the idea of “just add agents and things get easy.” Instead, enabling that sort of automation will require immense care in constraining changes to systems that cannot expose unsafe behavior. For example, one startup I know has represented their domain logic in a domain-specific language (DSL) that can be safely generated by an LLM, and are able to represent many customer-specific features solely through that DSL. Expanding the list of known-safe feature flags to make incidents remediable. To do this widely will require enforcing very specific requirements for how software is developed. Even doing this narrowly will require changes to ensure the known-safe feature flags remain safe as software is developed. Periodically reviewing incident statistics over time to ensure mean-time-to-resolution (MTTR) is decreasing. If the agent is truly working, this should decrease. If the agent isn’t driving a reduction in MTTR, then something is rotten in the details of the implementation. Even a very effective agent doesn’t relieve the responsibility of careful system design. Rather, agents are a multiplier on the quality of your system design: done well, agents can make you significantly more effective. Done poorly, they’ll only amplify your problems even more widely. Do AI Agents Represent Entirety of this Generation of AI? If you accept my definition that AI agents are any combination of LLMs and software, then I think it’s true that there’s not much this generation of AI can express that doesn’t fit this definition. I’d readily accept the argument that LLM is too narrow a term, and that perhaps foundational model would be a better term. My sense is that this is a place where frontier definitions and colloquial usage have deviated a bit. Closing thoughts LLMs and agents are powerful mechanisms. I think they will truly change how products are designed and how products work. An entire generation of software makers, and company executives, are in the midst of learning how these tools work. Software isn’t magic, it’s very logical, but what it can accomplish is magical. The same goes for agents and LLMs. The more we can accelerate that learning curve, the better for our industry.

15 hours ago 2 votes
Can tinygrad win?

This is not going to be a cakewalk like self driving cars. Most of comma’s competition is now out of business, taking billions and billions of dollars with it. Re: Tesla and FSD, we always expected Tesla to have the lead, but it’s not a winner take all market, it will look more like iOS vs Android. comma has been around for 10 years, is profitable, and is now growing rapidly. In self driving, most of the competition wasn’t even playing the right game. This isn’t how it is for ML frameworks. tinygrad’s competition is playing the right game, open source, and run by some quite smart people. But this is my second startup, so hopefully taking a bit more risk is appropriate. For comma to win, all it would take is people in 2016 being wrong about LIDAR, mapping, end to end, and hand coding, which hopefully we all agree now that they were. For tinygrad to win, it requires something much deeper to be wrong about software development in general. As it stands now, tinygrad is 14556 lines. Line count is not a perfect proxy for complexity, but when you have differences of multiple orders of magnitude, it might mean something. I asked ChatGPT to estimate the lines of code in PyTorch, JAX, and MLIR. JAX = 400k MLIR = 950k PyTorch = 3300k They range from one to two orders of magnitude off. And this isn’t even including all the libraries and drivers the other frameworks rely on, CUDA, cuBLAS, Triton, nccl, LLVM, etc…. tinygrad includes every single piece of code needed to drive an AMD RDNA3 GPU except for LLVM, and we plan to remove LLVM in a year or two as well. But so what? What does line count matter? One hypothesis is that tinygrad is only smaller because it’s not speed or feature competitive, and that if and when it becomes competitive, it will also be that many lines. But I just don’t think that’s true. tinygrad is already feature competitive, and for speed, I think the bitter lesson also applies to software. When you look at the machine learning ecosystem, you realize it’s just the same problems over and over again. The problem of multi machine, multi GPU, multi SM, multi ALU, cross machine memory scheduling, DRAM scheduling, SRAM scheduling, register scheduling, it’s all the same underlying problem at different scales. And yet, in all the current ecosystems, there are completely different codebases and libraries at each scale. I don’t think this stands. I suspect there is a simple formulation of the problem underlying all of the scheduling. Of course, this problem will be in NP and hard to optimize, but I’m betting the bitter lesson wins here. The goal of the tinygrad project is to abstract away everything except the absolute core problem in the cleanest way possible. This is why we need to replace everything. A model for the hardware is simple compared to a model for CUDA. If we succeed, tinygrad will not only be the fastest NN framework, but it will be under 25k lines all in, GPT-5 scale training job to MMIO on the PCIe bus! Here are the steps to get there: Expose the underlying search problem spanning several orders of magnitude. Due to the execution of neural networks not being data dependent, this problem is very amenable to search. Make sure your formulation is simple and complete. Fully capture all dimensions of the search space. The optimization goal is simple, run faster. Apply the state of the art in search. Burn compute. Use LLMs to guide. Use SAT solvers. Reinforcement learning. It doesn’t matter, there’s no way to cheat this goal. Just see if it runs faster. If this works, not only do we win with tinygrad, but hopefully people begin to rethink software in general. Of course, it’s a big if, this isn’t like comma where it was hard to lose. But if it wins… The main thing to watch is development speed. Our bet has to be that tinygrad’s development speed is outpacing the others. We have the AMD contract to train LLaMA 405B as fast as NVIDIA due in a year, let’s see if we succeed.

19 hours ago 1 votes
Explaining nil interface{} gotcha in Go

Explaining nil interface{} gotcha in Go A footgun In Go empty interface is an interface without any methods, typed as interface{}. A zero value of interface{} is nil: var v interface{} // compiler sets this to nil, you could explicitly write = nil if v == nil { fmt.Printf("v is nil\n") } else { fmt.Printf("v is NOT nil\n") } Try online This prints: v is nil. However, this sometimes trips people up: type Foo struct { } var v interface{} var nilFoo *Foo // implicilty initialized by compiler to nil if nilFoo == nil { fmt.Printf("nilFoo is nil.") } else { fmt.Printf("nilFoo is NOT nil.") } v = nilFoo if v == nil { fmt.Printf("v is nil\n") } else { fmt.Printf("v is NOT nil\n") } Try online This prints: nilFoo is nil. v is NOT nil. On surface level, this is wrong: t is a nil. We assigned a nil to v but it doesn’t equal to nil? How to check if interface{} is nil of any pointer type? func isNilPointer(i interface{}) bool { if i == nil { return false // interface itself is nil } v := reflect.ValueOf(i) return v.Kind() == reflect.Ptr && v.IsNil() } type Foo struct { } var pf *Foo var v interface{} = pf if isNilPointer(v) { fmt.Printf("v is nil pointer\n") } else { fmt.Printf("v is NOT nil pointer\n") } Try online Why There’s a reason for this perplexing behavior. nil is an abstract value. If you come from C/C++ or Java/C#, you might think that this is equivalent of NULL pointer or null reference. It isn’t. nil is a symbol that represents a zero value of pointers, channels, maps, slices. Logically interface{} combines type and value. You can think of it as a tuple (type, value). An uninitialized value of interface{} is a tuple without a type and value (no type, no value). In Go uninitialized value is zero value and since nil is an abstract value representing zero value for several types, it makes sense to use it for zero value of interface{}. So: zero value of interface{} is nil which is (no type, no value). When we assigned nilFoo to v, the value is (*Foo, nil). Are you surprised that (no type, no value) is not the same as (*Foo, nil)? To understand this gotcha, you have to understand two things. One: nil is an abstract value that only has a meaning in context. Consider this: var ch chan (bool) var m map[string]bool if ch == m { fmt.Printf("ch is equal to m\n") } Try online This snippet doesn’t even compile: Error:./prog.go:8:11: invalid operation: ch == m (mismatched types chan bool and map[string]bool). Both ch and m are nil but you can’t compare them because they are of different types. nil != nil because nil is an abstract concept, not an actual value. Two: nil value of interface{} is (no type, no value). Once you understand the above, you’ll understand why nil doesn’t compare to (type, nil) e.g. (*Foo, nil) or (map[string]bool, nil) or (int, 0) or (string, ""). Bad design or inevitable consequence of previous decisions? Many claim it’s a bad design. No-one describes what a better design would look like. Let’s play act a Go language designer. You’ve already designed concrete types, you came up with notion of zero value and created nil to denote zero value for pointers, channels, maps, slices. You’re now designing interface{} as a logical tuple of (type, value). The zero value is obviously (no type, no value). You have to figure how to represent the zero value. A different symbol for interface{} zero value Instead of using nil you could create a different symbol e.g. zeroInteface. You could then write: var v interface{} var v2 interface{} = &Foo{nil} var v3 interface{} = int(0) if v == zeroInteface { // this is true } if v2 == nil { // tihs is true } if v3 == nil { // is it true or not? } Is this a better design? I don’t think so. We don’t have zeroPointer, zeroMap, zeroChanel etc. so this breaks consistency. It sticks out like a sore zeroInterface. And v == nil is subtle. Not all values wrapped in an interface{} have zero value of nil. What should happen if you compare to (int, 0) given that 0 is zero value of int? Damn the consistency, let’s do what user expects You could ditch the strict logic of nil values and special case the if v == nil for interface{} to do what people superficially expect to happen. You then have to answer the question below: what happens when you do if (int, 0) == nil? The biggest issue is that you’ve lost ability to distinguish between (no type, no value) and (type, nil). They both compare to nil so how would you test for (no type, no value) but not (type, nil)? It doesn’t seem like a better design either. Your proposal Now that you understand the problem and seen two ideas for how to fix it, it’s your turn to design a better solution. I tried and the above 2 are the only ideas I had. We are boxed by existing notions of zero values and using nil to represent them. We could explore designs that re-think those assumptions but would that be Go anymore? It’s easy to complain that something is a bad design. It’s much harder, often impossible, to design something better.

yesterday 2 votes