This was written as part of the Pragmatic Engineer essay contest.

Software engineering has greatly evolved at Hinge over the past few years thanks to recent advances in agentic coding. Every month we incorporate new tools and techniques. We’re accomplishing engineering feats on timeframes that were impossible when I joined Hinge at the beginning of 2023.

The best part is that none of our techniques are complex or complicated. There is no magic. Everything is actionable and repeatable. If you work as a professional software engineer at an app or web company, you can likely adopt most of them. The only barriers are LLM budget, time, and a willingness to own the outcome. Honestly, many of you have probably adopted these, and you probably have your own strategies that you could share with us to make us move even faster. I’d love to hear them!

But first, let’s back up. Hinge is a dating app, and dating apps need humans in the loop. There are many reasons we can’t just hand Hinge over to the machines and go to lunch.

The first reason is taste. Imagine that you’re a software engineer working on tools for software engineers. It wouldn’t surprise me if you built and shipped a small feature within 30 minutes. If the problem were small and the solution were easy to verify, why would you need more time than that? But now imagine that you want to make the app resonate specifically for Gen Z users while still serving all of your existing users. That’s much harder, especially if you are in another demographic. Is there any human on this earth that can “build for themselves” across every national, political, gender, sexuality, and generational divide you can name? Absolutely not. You must work in aggregates. You need to perform a bunch of research. A lot of the research can be collected via surveys, but ultimately, you also need to find ways to interact directly with your audience and incorporate those ways into your process. Skilled designers and product leaders are needed to synthesize this information and propose new features. And you need that human touch and the taste that nails the vibe while straddling all of those lines.

Also, safety is critical to the experience on dating apps. Users tell us things that they might be afraid to tell their own family or declare publicly. So we must remain in control of our app’s experience because the stakes are too high. Ultimately, we recognize that we have ownership of what gets shipped into production, and we act accordingly.

It’s important to contextualize all of our LLM wins: they are done in an environment where there is real human impact, and human judgment still reigns supreme. And the results are really exciting. We have still found massive gains in four areas: prototyping, on-call incident response, tooling, and engineering. Let’s dive into each of these areas and go over specific techniques that have accelerated us.

Prototyping is easier than ever

A year ago, I joined two other recs engineers (and a talented MLE and one plucky user researcher) and together we built a recommender in four days for a hackathon. We vibecoded as hard as we could, we tuned it as hard as we could. Fueled by expertise and memes, we got our prototype over the finish line for second place. It was the seedling for a project that eventually hit production.

Earlier this year, we had another hackathon. I picked another recs project that had comparable backend difficulty to the previous year’s. But there was something different: I teamed up with two talented mobile engineers and I wrote the entire prototype recommender system by myself in three days while the mobile engineers built a custom UI to show it within the app. Good enough for third place, and it also was the seedling for a project that eventually hit production. Yes, this was a step backwards for podium finish, but it was a leap forward in that agents help us accomplish things on timelines that used to be out of reach.

Did I become a superhuman prototyper within a year? No way. The LLMs got better at generating prototypes. And when it’s easy for engineers to generate prototypes, engineers can get more product features into production. The old days are gone. You don’t need to craft an argument that’s so compelling that every person in the company bends their priorities out of the way so that your idea has room to be prioritized. You can just build something and show them, “hey, I think we should do this.” And if it’s good, people will pay attention.

This is happening at Hinge right now. I have done it. Others have done it. You can do it. If you have conviction that something should be built, fire off a prototype while you work.

You can make this an intentional part of your process. At Hinge, we’ve had success with official small teams with a few disciplines. At the beginning of the year, we were doing experimental machine learning model work, but we weren’t sure if we were on the right track. So we created our own Canterbury Tales cast of a Backend Engineer, a Machine Learning Engineer, a User Experience Researcher, and a Product Manager, and we rapidly iterated on prototypes with internal testers until we landed on a successful formula.

This approach has also succeeded outside of my domain of backend recommendations. Another team recently had a lot of success teaming up a Product Manager, Mobile Engineer, and Designer, and they faked the entire backend from the client as they continued to work on a UX-heavy experience.

There’s no right way to do it. Fake the backend for internal prototypes if you’re a frontend engineer. Fake the frontend if you’re a backend engineer. Just get something working and show people.

What happens after the prototype phase? Once the prototypes hit a critical mass of acceptance, they transitioned into more normal projects: Figma designs, Trust and Safety reviews, design docs, implementation sprints, etc. The prototypes are just that: prototypes. We borrow what we can from the prototypes and ensure that the shipped production system is implemented at a high quality.

Bots are helping out with on-call

Here’s a hypothetical incident: You hear a loud noise. Where are you? You’re in bed. Maybe it’ll stop if you don’t move. It’s still going. Then you find your phone and stare at it for a second. You’re being paged. It’s three in the morning. Now you remember all of those responsibilities you have. Right now, the most important one is that you’re on-call. You ACK the page and sit up in bed. You need to find your laptop. You go get it. You log on and go into Slack.

And then you click into @oncall-bot’s thread in the incident channel.

Here, you find a fully-automated attempt at an investigation. A critical service has an elevated gRPC failure rate. @oncall-bot notes that the service’s CPU is much higher than normal and the service hit its autoscaling limit and can no longer spin up new pods. It investigates a bit further. There are new stack traces coming from that endpoint. @oncall-bot examines the stack trace. An experimental feature was recently added in the code paths, and its code branches are taking too long for some users. @oncall-bot can’t figure out why it’s taking so long, though. You ask @oncall-bot to make a pull request to turn the experimental feature off while you double-check everything that it said. It all looks correct. You get an approval from another on-call engineer, you turn the feature off, you make sure the graphs normalize, and you go back to sleep. This is tomorrow’s problem.

We’re only at the beginning of our on-call automation journey, but the results are encouraging. By creating a bot with controlled access to service logs and metrics, and the ability to cross-reference its findings with code, our platform team created a good first responder. @oncall-bot isn’t the smartest bot in the world, but it does the first level of investigation that you’d do anyways. It often gets close enough to the root cause that the rest of the incident management goes much faster. Other times, it doesn’t uncover the issue but still provides a good jumping off point for investigation.

We’re starting to explore more automations like fixing common on-call issues, but we are early in this part of our journey.

Tooling is easier to build than ever

For years, testing out mobile builds at Hinge was an onerous process if you weren’t a mobile engineer. There were two approaches. If you wanted to install Xcode and Android Studio, you could build the app locally on the right branch and then run in an emulator. But many people who needed to test, like QA analysts, weren’t comfortable managing Git and running builds. So we would also publish a build to e.g. TestFlight so that they could test.

Two engineers on our platform team dreamed of a better way. Imagine a Mac app that just listed all of the active branches in both the Android and iOS repositories. In this dream it can automatically install the Android and iOS emulators for you. While we’re writing a wishlist, we’d also add that we could easily publish new builds to it, and it was so easy to use that nontechnical people could use it too.

There was a problem. Neither of them knew Mac development.

OK, I’m kidding. It wasn’t a huge problem because they could lean on LLMs to do the Mac development. They built it. It’s awesome. You double-click a build and the emulator just runs with the right version. Everything is handled behind the scenes for you.

This isn’t the only instance of new tooling cropping up at Hinge. We have a Cambrian explosion of new tools like faster deploy scripts, new ways to test our code, knowledge bases, CLI tools, etc. We had all of these ideas in our head, but we didn’t have enough time to realize them. But now that you can just have an agent chew on it in the background, there isn’t a barrier anymore.

What other problems did we have a few years ago that don’t exist anymore? When I joined, I was given a huge Notion checklist of instructions. Each line of instructions linked to another large Notion page that explained the relevant setup of some small facet of the system. It took a long time and inevitably you’d still lack a permission or three you needed.

Well, those are also the days of the past. Now we have a CLI tool named dev that can set up any number of environments for you. Do you want to program iOS, Android, and backend? Done. What is your LLM of choice and what MCPs do you want to have installed? Done.

It drifts sometimes. Nothing’s perfect. But it’s easy to just point an agent at it and say “fix it.”

We’ve also seen wins from MCP. The most obvious is our notifications platform MCP, which can also be accessed via its corresponding Slack bot. It’s a one-stop shop for notifications use cases. If you’re an engineer that has never worked with notifications, just describe what you need and it’ll explain everything you need to get started. If you want metrics on a running campaign, it can look them up. If you need to investigate why a notification isn’t behaving like you expected, it can help you.

Accelerating design, coding, and reviews

So you’ve researched, you’ve prototyped, you have design mockups, and now you have something that you want to build.

Now the engineering starts.

And this is where there is a devil sitting on your shoulder. He leans over to whisper in your ear. And you already know what he wants to say. “Why can’t you just generate the tech spec from the product spec? And why can’t you just implement the code directly from the product spec? How do you even benefit from the tech spec?”

I’ve personally settled on my own heuristic for whether “slop” writing is acceptable in a particular situation: “Did your org care about this kind of writing before LLMs?” At Hinge, we took a lot of care to craft and write design docs. We wanted to ship good architecture and reasonably good code. We were willing to discuss the docs and iterate towards good solutions.

But on the other hand, little attention was given to commit messages. In fact, I don’t think I’ve ever seen someone asked to rewrite a commit message before it would be approved. So this is reflected in my own work: I make sure the docs are written in my own words, but I let the LLM generate my commit messages. When writing this I feel a lot of conflict from imaginary engineers. “It should all be this one way” or “it should all be another way.” But ultimately I think you need to be pragmatic. Different text fields add different amounts of value. Sometimes it needs to be good enough, and sometimes it’s worth spending extra time to keep revising to get it perfect.

So ultimately, the design docs are all written in my own words, even though they are heavily accelerated by LLMs. I’ve tried everything from “Go straight from description to feature implementation” in hackathons, to “let’s generate the design doc with the LLM and see how it goes,” to “let’s just write the design doc like we used to.” And I find that I get better results when I write the design. I internalize my own designs far better when I produce them in some way instead of just reading them. I think you will find the same thing: taking time to internalize your designs will make you a stronger engineer as you continue to build on your own work in the future. Sometimes you need to slow down a little to go much faster in the long run.

And here’s the cool part: you can have the LLM check your tech spec against the product spec and Slack conversations for drift. You’ll find tons of small drift, where the LLM finds that the product spec and tech spec say one thing, but a Slack conversation indicates that some people believe something else. And every one of those is a potential deadline slip that just got highlighted.

I’ve also found that LLMs are amazing at decomposing design docs into sensible sequenced tickets. I’ve experimented with just having the LLM create all of the backend tickets, and it has worked out. The tickets have enough context that an engineer can easily understand the scope and how it fits into the project. LLMs are also great at picking up these tickets and producing a reasonable first draft.

OK, we have tickets and we’re implementing! And this is where people have their own preferred approaches. LLM coding has ended up a lot like IDEs used to be: everyone has their own distinct styles. Some people love the Superpowers plugin and checking in all the specs, some people love centralized knowledge bases. I personally have my own vibecoded workspace switcher for working on many tickets in parallel.

We also have collaborative ways to work. For example, we have a Slack bot that can author a pull request on your behalf from the surrounding context. We typically use this for small changes that fall out of Slack discussions.

Token spend is capped, but you can easily request more if you provide rationale. The goal is to stop unexpected runaway usage.

There’s no single silver bullet that has made us more efficient. It’s just a collection of lots of small efficiencies. Here’s an example: creating a new service was tedious when I joined Hinge in 2023. It was difficult to understand what pre-existing patterns were modern or obsolete, so you made a lot of judgment calls on the fly trying to pick out the most modern patterns. Then you’d write the Terraform, which had a combination of obsolete or overridden fields that had no side effects, or alternatively seemingly-harmless fields that were unexpectedly important. Typically you’d need several attempts to stand the service up in our development environment, and then several attempts to stand the service up in our production environment, just to work through all of the random configuration issues.

This is completely different when I’m writing this in the second half of 2026. You can simply tell your LLM, “write a new service named servicename. Pattern it after preexistingservice. Connect to serviceone and servicetwo’s gRPC servers, and set up the skeleton of a gRPC service but don’t add any endpoints yet.” and it’ll one-shot it. And then you can go to the Terraform and invoke the relevant skill to create the Terraform for the service. You can just point an agent at both directories and ask it to double-check the work before you send it for review.

In fact, don’t sleep on skills. They are a high leverage way to accelerate your work. We are encoding more and more Hinge-specific functionality into skills and I continue to see it pay off. Here’s another example: today I wanted to test a new set of public-facing endpoints that I’ve been building. So I told my agent to test them with my development user ID. And the agent found a relevant skill for issuing these commands against the dev server. From there the agent understood that it had access to a CLI. This CLI produces tokens that auth the request. And then the skill explained how to find the request format. And before I knew it, the agent had exercised every endpoint and printed a table of results. Then I asked it to exercise the content filter. And good grief, agents can curse with the best of them.

So you’ve written your code, and now you’re ready for code review. But a human isn’t going to read it yet. We have a specialized review agent that should be satisfied before a human reviewer is tasked with looking at the pull request. We liked it for two reasons: first, it has a comparatively low false positive rate. Second, it’s just a legitimately good first-pass review bot. It finds issues that frontier models don’t. It finds issues that the human reviewer doesn’t. But you will also find issues that slip past every single bot. And this is where it’s important that you exercise your own judgment as a human reviewer. Every day, I find problems that the agents miss. The agents can perform incredible feats of engineering, but ultimately they don’t produce the work by themselves. They do this as a way of accelerating you and your organization.

And then when you’re finally finished with coding, I highly recommend running one final consistency check. Prompt your model, “Here are the paths to my backend and client codebases. Here’s the path to the tech spec, read it with the MCP. Here’s the path to the product spec, read it with the MCP. Check MyNewFeature for integration problems between them or inconsistencies between spec and implementation.” It helps find those last issues that would come up in pre-release QA and internal dogfooding.

You can just try things

Hinge has adopted many techniques that have added up to one thing: software engineering is completely unrecognizable from the way it was a few years ago. And there’s a lot of room for more change.

We didn’t adopt everything at once. One feature at a time, we gave them a shot. We tried many techniques and technologies that didn’t work for us. Many sucked for a while until they finally reached a threshold where they became great. But each time you find a working technique, things get a little better, a little faster, or a little safer.

So I really encourage you to keep trying techniques that you hear about. Many have a high skill ceiling and you can get better at them. Many are just distractions that won’t work for you. But the more experience that you get, the more you can differentiate between skills and distractions and continue to improve engineering in your organization.

Thanks to Sammy Spiegel, Jarryd Boyd, Lisa Detig, and Sam Rozenberg for providing feedback on drafts of this essay contest entry.