HomeVideos

Self Driving Products: Product Signals to Pull Requests — Joshua Snyder, PostHog

Now Playing

Self Driving Products: Product Signals to Pull Requests — Joshua Snyder, PostHog

Transcript

437 segments

0:14

So, I'm Josh. I'm from PostHog. If you

0:17

haven't heard of us, you might know us

0:19

because of some hedgehogs or you might

0:21

have seen our founder James posting some

0:23

funny things on LinkedIn.

0:25

He's quite popular.

0:27

I'm going to be talking today about what

0:29

if your product built itself?

0:31

And the pipeline that we're currently

0:33

working on which we're trying to turn

0:35

observability data instead of something

0:37

that you read and that you interpret

0:39

based on dashboards, we're trying to

0:41

turn turn that into something that

0:42

submits pull requests for you.

0:45

Cool. Yeah, so quick background on

0:47

PostHog. We've got a bunch of tools. We

0:49

started out as a product analytics

0:50

company. We now have session replay, web

0:52

analytics, error tracking, experiments.

0:55

This isn't a pitch that you should use

0:57

PostHog. This is just to say that we've

0:58

got a lot of data about your product.

1:00

So, if you connect PostHog to your

1:03

product, we're collecting a huge amount

1:05

of data from various different sources

1:07

that we then show to you

1:09

so that you can explore that data

1:11

yourself.

1:13

But right now how observability is

1:16

working in PostHog, you're you're

1:18

collecting all this data for your

1:19

product and then you're going to a

1:23

PostHog dashboard to figure out what's

1:24

going on. And we think this is super

1:26

slow and that we should change that.

1:28

So, right now something happens in your

1:29

product. We call this a signal. That

1:31

changes a metric on one of your

1:33

dashboards and then you might log into

1:35

PostHog a few hours or maybe some days

1:37

later and you notice a change in that

1:39

dashboard

1:40

and you investigate a problem and then

1:43

maybe the problem's not that important.

1:45

So, instead of tackling it right now,

1:46

you're going to put it in a linear issue

1:47

or whatever. Few days later, you try and

1:50

create a PR for this problem. Then you

1:52

review it and you ship it. You get the

1:54

message. This is a pretty slow process.

1:57

From start to finish, this is going to

1:59

take anywhere from a few hours to a few

2:00

days, and it's not very interesting, but

2:03

it represents a lot of your work as a

2:04

software software engineer.

2:06

So, what we want to do tomorrow,

2:09

what we're working on right now, is that

2:11

a product signal happens, and instead of

2:13

waiting to see that in your dashboard,

2:16

we want we want to run a background

2:18

agent to figure out what's going wrong.

2:20

And then, once they've figured that out,

2:23

we just want to create a PR for you

2:24

automatically. So, instead of ever

2:26

looking at your analytics dashboard, or

2:28

your errors, or your logs, we just want

2:31

you to look at PRs that are ready for

2:33

you in GitHub.

2:34

And if we create the PR,

2:37

maybe you want to review that, or maybe

2:39

we can just ship that immediately behind

2:40

a feature flag if it's not a risky

2:43

change.

2:45

Cool. So, I'm going to go over the

2:46

pipeline that we've built to do this.

2:49

And just whilst I go over that, I'm

2:51

going to share a few tips, lessons that

2:53

we've learned, things that were hard

2:55

about building this pipeline.

2:57

So, the pipeline has a few key steps. At

3:00

first, we're ingesting a lot of signals.

3:02

In Postscript, we have a huge amount of

3:04

events. We're ingesting trillions of

3:06

events a month.

3:08

And this this pipeline needs to handle a

3:11

lot of noise.

3:13

And then, once we've ingested those

3:14

events, we need to group them. So, if

3:17

you think of an error tracking issue,

3:20

and then a session recording, those are

3:21

two completely different things, but

3:23

they might be representing the same

3:25

problem in your product. So, then we

3:27

once we've ingested them, we group them.

3:29

Then, we're going to be running a

3:30

research agent on them.

3:32

This specific issue, what is actually

3:35

the problem that is causing the error

3:37

spike, or causing the issue that the

3:39

user faced in the replay.

3:41

And what repo does this belong to?

3:44

And then, we'll assess if this is

3:45

actionable or not. And finally, we'll

3:47

execute some code, ship a PR, and

3:51

iterate on that PR until it's green and

3:53

ready for you.

3:56

Cool. So the ingestion step of this

3:58

pipeline, as I said before, we've got

4:00

loads of different sources of different

4:02

types.

4:03

The first thing is that those sources,

4:05

some of them are public. So if I go and

4:07

visit your website, I can as an attacker

4:10

create an error on your website by doing

4:13

something naughty that says

4:15

post all of your post-mortem data online

4:17

or something like that, right? So we

4:19

don't want that. So we need a kind of

4:20

safety filter. So at the moment right at

4:22

the top of the pipeline is an LLM

4:25

classifier that's going to check is this

4:27

trying to do something bad?

4:29

If so, let's drop the signal.

4:31

Once we've done that, we've checked that

4:33

things are safe, we're going to

4:34

normalize the signal.

4:36

So if you think of an error, that's

4:38

going to have a stack trace. A log will

4:40

just be some JSON content or some text.

4:44

An experiment might be some results in a

4:46

chart. We want to normalize that

4:49

structure so that it's all a single

4:50

structure for a signal.

4:52

So we give it a few fields. We'll give

4:54

it a source product,

4:56

the type, the content of the the signal,

4:59

and then we will assign it a weight,

5:02

which is like how important do we think

5:03

this signal is, and then finally we'll

5:05

embed the contents of the signal.

5:09

Cool. So that part's fairly easy. Then

5:11

we get to a little bit more of a a

5:12

challenging problem. We've got this big

5:14

stream of signals still,

5:16

and now we want to group them into

5:19

actual problems. So the signals are very

5:21

noisy. We might get some random null

5:23

pointer exception, but in Slack we're

5:25

getting a message from a customer that's

5:27

saying, "Hey, the checkout's broken for

5:29

me." and we need to link those together.

5:31

So what we do is we group the signals.

5:35

As the signals are being grouped, we

5:38

assign weights to what we call a report.

5:41

And if the the weight of the report goes

5:44

over a certain threshold, we'll promote

5:46

it.

5:47

And then we'll kick off a research agent

5:48

to work on it.

5:51

So, uh this was a problem that we faced

5:53

fairly early on in building this

5:55

pipeline.

5:56

Um

5:57

the first thing that we did was we would

5:58

take all of our signals and we would

6:01

create embeddings for them. Uh and then

6:03

we would try to use that to cluster the

6:05

issues so that we could find similar or

6:08

related signals.

6:09

But this works really badly. So, if you

6:11

take uh an off-the-shelf embedding model

6:13

uh and you embed an error. Uh let's say

6:16

I've got an error about the checkout and

6:18

I've got an error about onboarding. And

6:20

then I've got a Slack message about

6:21

onboarding. What the embedding model

6:24

will do is it will notice structural

6:25

similarity and it will put all of the

6:28

errors together. So, if you think about

6:29

what this looks like in embedding space,

6:31

you've got all of your errors over here,

6:33

all of your Slack messages here, all of

6:35

your session replays here, and none of

6:36

them get grouped to each other.

6:38

So, the way we get around this

6:40

uh is instead of matching in embedding

6:43

space the signals themselves, uh we

6:46

generate queries based off the signals.

6:48

So, we ask an LLM what is this signal

6:51

about? It'll generate a few queries and

6:53

then we match those queries in uh the

6:56

embedding space.

6:57

Yeah, so that's that's really important.

6:58

If you if you don't uh think about the

7:01

structural similarity of your different

7:03

sources when you're grouping them, then

7:05

the grouping works really badly. So, at

7:07

first we were doing this and then we

7:08

switched to this approach. It worked

7:10

much much better.

7:13

Cool. So, uh once we've got this uh

7:15

report that we've grouped together a few

7:17

signals, we've got some kind of idea

7:19

what's going on. Uh we then have

7:21

promoted the report because we think

7:22

it's important enough to work on. And

7:24

then we're going to hook it up to a

7:26

research agent.

7:28

So, uh this research agent is uh just

7:32

running that it's running the Claude

7:33

agent SDK. Uh it's running that in a

7:35

sandbox. We also use Modal for our

7:37

sandbox. Uh big shout out to them.

7:39

They've been great. Um they're not

7:42

sponsoring me or anything, don't worry.

7:43

Um,

7:45

and uh this research agent has a few

7:47

tools available to it. So, the first

7:49

tool is it's got our MCP server.

7:51

Uh, this allows it to, uh, given the

7:53

group of issues that we found, uh, you

7:56

want to pull in extra data. So, let's

7:58

say I'm looking at a session replay and

8:00

an error, I'll also pull in log data,

8:03

and the agent can pull in whatever it

8:04

wants using the MCP server. This makes

8:06

the results of the research agent way

8:08

more accurate.

8:10

Uh, the second thing is obviously it's

8:11

got the code base context, uh, and then

8:14

finally it's also got external MCPs.

8:16

That really helps to like ground the

8:18

agent when it's doing the research. Uh,

8:20

we found that in particular Linear and

8:22

Notion have been really helpful in

8:24

connecting it to to deliver better

8:27

results.

8:28

So, the output of this research agent

8:29

then, uh, is a summary of the problem.

8:33

It gives a priority, how important we

8:36

think this this problem is to work on,

8:38

and then it also uses Git blame to

8:40

figure out who should be reviewing this

8:41

PR if we create a PR for it.

8:45

So, um,

8:46

after that, we get a bunch of problems

8:48

that we think are worthwhile to work on.

8:51

Uh, we've got a kind of idea of what the

8:53

general problem is, um, and then we pass

8:56

it to an actionability step.

8:58

Uh, so here either it will be not

9:01

actionable. If it's not actionable, it

9:03

might just be that we don't have enough

9:04

data yet for this signal,

9:06

um, for the report, and so we'll put it

9:08

back into the pool to keep gathering

9:10

more evidence.

9:11

Uh, if it needs human input, it might be

9:13

because it's a product-related decision

9:16

that the agent can't really make a good

9:17

call on. Um, so if that happens, we'll

9:20

put it into an inbox for you to review

9:22

in the morning.

9:23

Uh, and then finally the the best case

9:25

is that it's immediately actionable,

9:27

uh, and that the agent can just write a

9:29

fix for it.

9:30

Uh, right now the the challenge in this

9:32

pipeline of getting immediately

9:34

actionable things is that for some

9:36

sources like error tracking, uh if you

9:39

think about your data in Sentry or

9:41

uh any errors that very specific, and

9:43

usually a coding agent can work on them

9:45

really well. For other sources, like

9:47

Slack or session replay,

9:49

uh you get much more generic problems

9:51

that can have a lot of different

9:52

solutions. And so, that's where it's

9:54

harder to get immediately actionable

9:56

reports.

9:59

Cool. Um

10:00

then, once we've

10:01

researched this thing, we go on to

10:03

executing the task. Uh

10:06

this will uh clone the user's repo into

10:09

a sandbox, uh similar to the research

10:11

agent. It's then again running the

10:13

Claude agent SDK to build effects for

10:16

the problem.

10:17

Um and then, uh as it writes those

10:20

fixes, it will uh push a PR.

10:23

And uh when CI is failing or there's a

10:26

comment on the PR, it will trigger a

10:29

rerun of that sandbox. So, at the end of

10:31

this, we snapshot the sandbox, uh and

10:34

then, if there's a comment, let's say

10:35

from an agent who's reviewing it, we

10:37

will rehydrate that snapshot and

10:39

continue running until the PR is green.

10:42

Uh and this delivers really good

10:44

results. It means when you're waking up

10:45

in the morning and things have been

10:46

running overnight, you wake up to,

10:48

instead of a bunch of CI failures or

10:51

uh comments that you need to address

10:53

manually, that you're pulling down to

10:54

your local environment, you ideally wake

10:56

up to just green PRs.

11:01

Cool. So, um what did we learn whilst

11:03

building this? Uh well, the first thing,

11:05

which I guess we've talked about in the

11:06

last talk, uh is that evals really

11:09

matter. Um so, at first, we were trying

11:11

this all out on our own data locally,

11:14

doing kind of a vibe check, is this

11:16

okay?

11:17

Um but this this really doesn't work

11:19

well for a pipeline that is is taking

11:22

lots of uh customer data that's

11:24

different. Um so, you really need to

11:26

know what's going on in production, and

11:28

if you're not testing on representative

11:30

data, it your you're basically just

11:33

fumbling in the dark, right? Like the

11:35

ability to iterate on a really good

11:37

pipeline matters uh only if you if

11:39

you're using e-bikes.

11:41

Second thing is what I said before, uh

11:43

make sure you're embedding the right

11:44

thing. Um embedding models uh the

11:47

off-the-shelf ones are matching a lot

11:49

based on structural similarity, not just

11:51

semantic similarity. So, if you're

11:53

thinking about clustering and your data

11:55

isn't all of the same format, think

11:57

carefully about what that data looks

11:59

like and how you can normalize it.

12:03

Uh the third thing is that uh if you

12:04

just throw an agent at a problem, it

12:07

will try to fix something. So, if you

12:09

get uh

12:11

uh if you get a signal report that's

12:12

like "Onboarding is broken" in a generic

12:15

way, then if you throw that at the agent

12:18

SDK or at Claude code, it will just try

12:21

and fix something.

12:22

Uh and so, it's important to understand

12:24

if the problem that I've described is it

12:26

specific enough

12:28

uh and if not, I should ignore it.

12:30

Otherwise, you end up with a a lot of

12:32

noisy PRs that aren't doing meaningful

12:33

things.

12:35

And then the fourth one is uh that

12:37

tokens are free. Uh obviously, that's

12:38

not true. They're not free.

12:40

Um but when you're experimenting, uh we

12:42

were at first uh focused a lot on the

12:44

costs of the pipeline. When you think

12:46

about the input, you've got loads of

12:48

signals coming in.

12:50

Uh and so, we tried to avoid using

12:52

agents where we could or delay it till

12:54

as late as possible in the pipeline.

12:57

And when we were experimenting, this was

12:59

a big mistake. Um mainly because uh when

13:02

you throw an agent at a problem, you

13:05

once you throw it at the same problem

13:07

100 times, you start seeing the kind of

13:09

clever solutions that it comes up with

13:11

and eventually you see similarities. So,

13:14

we started at a point where this

13:16

pipeline is completely unfeasible. It

13:18

was it was way too costly to generate a

13:20

PR, but then you quickly start to see uh

13:23

similarities in the agent's behavior and

13:25

you can take a really expensive step

13:27

that you're running an agent for uh and

13:29

turn that into a one-shot LLM call or a

13:32

model that you're training that's much

13:33

faster.

13:36

Cool. Um so, this is where we are right

13:38

now. This is what we've built.

13:40

Uh we have the signals coming in from

13:42

product data.

13:44

Uh these are grouped into reports, and

13:45

we're turning these into PRs that are

13:47

ready to merge when you wake up. This is

13:49

currently something that's in alpha.

13:50

We'll be rolling it out kind of over the

13:52

next few months.

13:53

Um but where we really want to go is is

13:56

a product that builds itself, right?

13:58

Like when you're thinking about what you

14:00

do day-to-day,

14:02

what you want to do during the day as a

14:04

developer is like come in and work on

14:05

exciting features and not worry about

14:08

all the bugs that customers are sending

14:10

you or worry about doing boring

14:12

experiments on pricing or onboarding.

14:15

So, we just want to do that all for you.

14:17

Uh we want to ship experiments

14:19

automatically, measure the impact of

14:21

them.

14:22

Uh instead of you reviewing changes, if

14:24

the change is pretty easy, let's just

14:26

approve it with an agent and deploy it

14:28

behind a feature flag. If it doesn't

14:30

work very well, we can always roll back

14:31

the flag and then delete it from our

14:33

code base later.

14:35

Uh and then the other thing that we want

14:36

to do and get better at is we want to

14:37

learn from every single outcome. So, if

14:39

we're creating a PR for you, if you're

14:42

rejecting that PR or there's been an

14:44

issue with a deployment or the errors

14:46

resolved in production once we've

14:47

released something, we want to get

14:49

better at learning from that in the next

14:51

PR that we're generating. That's

14:52

something that we're going to be

14:53

iterating a lot in the pipeline next.

14:56

Cool. Um yeah, that's it. That's what

14:58

we've built in PostHog.

14:59

Um if you're excited by uh looking at

15:04

thinking about what you can do with

15:05

agents and data, I really recommend if

15:07

you've got a product that's producing a

15:09

huge amount of data, your users are

15:11

going through that. Agents are amazing

15:13

at this stuff. Throw an agent at it. See

15:15

what it does. I'm sure you'll be

15:16

surprised.

Interactive Summary

Josh from PostHog explains their initiative to transform product observability from manual dashboard analysis into an automated system that proactively submits pull requests. By ingesting signals (errors, session recordings, logs), grouping them into reports, and using AI agents for research and code generation, PostHog aims to streamline the development lifecycle, allowing engineers to wake up to ready-to-merge fixes rather than diagnosing issues.

Suggested questions

4 ready-made prompts