Hacker News new | ask | show | jobs
by jasonlaster11 1736 days ago
Co-founder here. It feels incredible to be sharing Replay with all of you. It's been a labor of love the past five years!

Replay started off as a simple experiment in what would happen if we added a step back button and rewind button to the Debugger. We quickly realized two things. First, nobody uses breakpoints. Second, being able to share is so much more powerful than being able to rewind.

Here’s how Replay works today. Somebody on the team records a bug with the Replay Browser and shares the replay url with the team. From there, developers jump in and add print statements. The logs appear in the Console immediately so you don’t need to refresh and reproduce a thing.

Over the past year we’ve talked to hundreds of users, recorded 2.5 million replays, and worked incredibly hard to ensure Replay would be fast, secure, and robust from the get go.

Want to check it out? You can download Replay today. Can’t wait to hear what you think!

Interested in learning more, here is our announcement blog post https://medium.com/replay-io/launching-replay-the-time-trave...

16 comments

The problem with breakpoints is my loop runs 1000 times and I only care about the one time it errors. Making watch logic for that sometimes is too complicated or changes the outcome (race conditions especially). This seems like a great solution. I'll be checking it out!
If you can trigger a debugger from a keyword it's usually pretty easy to trigger it conditionally or on an exception in the code rather than through a GUI.

That works well for ruby and javascript and probably lots of others.

e.g. something like the equivalent of

  def foo
    bar.map do |b|
     b.do_something!
     debugger if b.state == :something_youre_interested_in
    end
  rescue => e
   debugger
  end

I'm really excited by Replay. I think it will be invaluable.
Yes, but I think this is what they meant by

> Making watch logic for that sometimes is too complicated or changes the outcome (race conditions especially).

Why is that better than using your IDE? Your writing debugging code directly in you production code...
I haven't personally used Replay, but from my experience using rr (a native debugger that also provides time-traveling features) being able to replay execution both backwards and forwards in time on a whim is amazing. These tools excel at diagnosing bugs that are hard to reproduce, because you only have to reproduce the bug once under the debugger and then you can endlessly replay that execution until you figure it out. As Jason said above, you can retroactively add print statements in places that would be useful, without having to waste time trying to reproduce the bug again!

roc (the original author of rr) founded a company to build an even more compelling product on top of rr called Pernosco. They have some mind-blowing demos I'd recommend you check out: https://pernos.co/ .

Being able to easily answer questions like "where did the value in this variable come from, and when did it get set?" makes debugging a wildly different experience.

How would you debug something inside of a loop (1000s of values) with a line breakpoint in your IDE? Your debugger will trigger the breakpoint every single time it walks through the loop, even when everything is going fine. Now if you conditionally trigger the breakpoint inside the loop using code, it will trigger once and only if it encounters whatever error you want to catch.

Obviously you remove that debug statement from your code after you're finished debugging.

Browsers have conditional breakpoints available in their tools/debuggers so you can write them without changing the code.
Err, conditional breakpoints are your friend.

Also the GP claims "nobody" uses breakpoints? Everyone in my team uses them all the time.

Suppose it all depends on your language/tool chain, in my experience breakpoints are very commonly used by developers in visual studio.

I assume he referred to breakpoints in the browser.
Chrome does conditional breakpoints - just edit the breakpoint and type your condition in - but it massively slows down the page of that code gets hit a lot.

So much so that I prefer to put the condition in my local build as source code then put a normal breakpoint on that after it has rebuilt (a few millseconds with a good watcher)

I use it ALL the time. Not sure how people are debugging code nowadays?
Which debugger do you use because I live in JetBrains tools and their debugger front-end support for conditional or on exception break points is phenomenal
What I mean is coming up with a condition to make as a watch/breakpoint isn't always obvious. Especially with errors that happen "sometimes randomly".
That’s exactly where replay should shine. Stick a breakpoint in your error handler, then rewind when it breaks.
Actually it might be even easier — if your specific error shows up in the console, there's a handy rewind button right beside it :)
If an error causes an exception there is an option to suspend execution when one is thrown.
But typically it will throw an exception _after_ the interesting code happened. For instance it might throw a NullPointerException, but you want to know what set it to null in the first place. With breakpoints you have to suspend on exception, and then add additional breakpoints earlier in the code, and then rerun the code. With rewind you can just step backwards.
The debugger in devtools also has these capabilities.
Can confirm. Use it often both on Chrome and Firefox.

I love the idea of reply, but is it targeting the wrong people to get the post leverage out of it? Print statements generally don't need to be used for debugging if you know how to use a debugger. Which really isn't difficult if you spend maybe half an hour learning it's features.

For Python, but only if execution is deterministic, I made a small library that tries to address that issue: https://github.com/breuleux/breakword (it prints data alongside a deterministic list of words and you can set a breakpoint on a word of interest in a second run).

It's really a poor man's replay, though. That tool looks really slick, I'll definitely give the Python version a go if/when it comes!

Err, conditional breakpoints are your friend.

Also, "nobody" uses breakpoints? Everyone in my team uses them all the time.

Perhaps the comment was a generalization and not a attack on your process. Its not a radical claim to suggest breakpoints are not as popular as console.logs
> First, nobody uses breakpoints.

It saddens me that a lot of people don't use debuggers and default to adding print statements. As far as I can tell, it's for several reasons:

1. The debugger is primitive (e.g. Godot GDScript - no conditional breakpoints or watches).

2. The debugger is unstable (e.g. Android Studio - frequently hangs, or takes a long time to populate data needlessly)

3. The debugger's UI is not friendly (e.g. Android Studio - hitting a breakpoint in multiple threads causes unexpected jumps or loss of current state; VSCode C++ debugger - doesn't display information properly or easily (arrays of objects) or displays too much information (CPU registers, flags, memory addresses); C++ debugger for D - doesn't display D data types).

4. The debugger is not properly integrated into the environment - can't find symbols, libraries or source files, or finds the wrong source files, etc. Need to jump through hoops to configure those.

5. Platforms don't support debuggers properly (e.g. again Android - ANRs when debugging the main thread, can't leave a debugging session overnight without some timer killing the process)

6. Developers got used to the workflow of "add a print statement, rerun and check the console" since high school and nobody taught them a more powerful tool

7. Developers code all day, so adding print statements by coding feels more natural than switching to the debugger's UI and way of doing things. (e.g. "if (i == 100) console.log(value)" allows you to stay in the same code, as opposed to setting a breakpoint, finding out how to add the 'i == 100' condition and pray that there's no issue with variables being optimized out at runtime).

I like Replay's features and that it's improving the state of the current tools. At the end of the day, adding print statements in Replay doesn't seem to affect the state of the application, so in that sense it's similar to gdb commands in that it's just a UI choice, but I wouldn't go as far as encouraging print-based debugging.

Outside of Replay, print-based debugging is still a primitive way of analyzing the state of the app and promoting this state of affairs reduces the pool of people who use and would hopefully improve the existing debuggers.

We all appreciated Firebug and the Chrome DevTools because of the powerful features they give us to inspect the state of the application. Imagine a person who adds print statements to their code every time they want to inspect the DOM or check the current CSS attributes. It works, but we have better tools, and we should make them even better.

I think print statements are actually useful in ways that typical debuggers are not meant to be; they make it easy to show changes over time, and they provide a tight feedback loop between observing the value of some data and performing interactions that update that data. For example, if you wanted to know how a coordinate calculation changed as you scrolled the page, print statements would be more useful than a debugger. I don't think this is exclusively why debuggers get less use, but I think that print statements aren't inherently a thing to optimize away from.
That and concurrent execution is where I've found print statements to be most useful, but nothing prevents a debugger from keeping track of some value over time and then display those values on the UI, just like one would with a print statement.

My view is that using print statements is absolutely a subpar method of debugging and that we should, in fact, optimize away from it by creating better debuggers.

Anything you can do with a print statement can also be done with a logpoint, if your debugger has that concept. Logpoints can also sometimes be simulated with conditional breakpoints (log something and then return false).

The debugger saves so much time wasted recompiling/reloading with new print statements, IMO it's strictly better on every aspect.

I've been dreaming forever about writing a debugger that basically just produces a timeline/log (branching in the case of threads or processes) of program execution, you can drill down the stack or into a loop at any point, and surfaces the trace of your code as opposed to 17 layers of library indirection.
I mentioned this in a different thread, but I'd recommend you take a look at Pernosco, a debugging tool written by the original author of rr: https://pernos.co/about/callees/
Couldn't agree more. Debugger support in modern codebases has become a huge after-thought which is such a shame.

It is an amazing way to discover how a codebase works. You pick a point of interest, and then you get the entire path from the beginning of the app's execution to that point as your stack trace, and every variable along the way too. Watches are great too for tracking a value changing over time.

Micro-services and Docker also took debugging many steps backwards - one advantage of a monolith is that you can easily step-through the entire execution, whereas if you have to cross process-boundaries it becomes a lot more complex to properly debug.

I'm working on a monorepo template at the moment where everything is debuggable with a single-click. This includes debugging native addons in C++ and Rust for Node.js projects. It's not easy - which is why people avoid debuggers so much.

I recently setup debugging in a Rust project for IntelliJ was the alternative was adding `dbg()!` statements which involved 10s recomplilation. The difficulty was implementing the right pretty-printers in lldb so you could see the value of various types because support is quite immature at the moment.

Those top-to-bottom stack traces also become a lot less useful in today's highly-meta frameworks, where functions get passed around and eventually scheduled at a point totally divorced from where they live in the code. I'm not saying this is a bad thing, it just makes debuggers somewhat less useful.
It's certainly a combination of these things. I use breakpoints all the time when I'm working with C# because I'm inside Visual Studio. It's super easy to work with the debugger there. With Source Link I can even step into other libraries of ours. Debugging C++ under VS is also not bad, and Python in PyCharm is a good experience.

But if I don't have VS or PyCharm available, I'll switch to printf debugging.

Though there are some cases where even with a good debugger I'll end up debugging by modifying the code. Sometimes it's necessary for performance reasons. Conditional breakpoints when debugging C# are extremely expensive so tossing one on a line that's executed many times may make the process far too slow. In that case it's better to compile in an if statement and then drop the breakpoint inside there. Other times the debugger is just limited in what information it can provide. Pointers to arrays in C++ are a common annoyance since the debugger has no length information.

My theory is that breakpoints are not useful because they let you go forward. But if you have an issue somewhere where a variable is not in the right state, it's because somewhere in the past was the issue. But you can't go back with a normal debugger.

Replay allows you to go back in time which is to me the biggest breakthrough. This actually makes them useful!

Breakpoints are a tool to stop execution and land in the present. It's the debugger that decides where you can go from there. Typically they'll allow you to go into the past, but only to inspect the stack frames, because the values on the heap get overwritten. I vaguely remember that some debuggers are able to record heap writes and thus are able to show the entire state of the app at each frame, effectively "going back" and replaying stack frames. My guess is that Replay does something similar.
Maybe 2a. Executing code (like a print) in an auto-continuing breakpoint action makes the program itself pause; especially tiresome when you're looking at a timing or performance issue.
Just my anecdote: Personally I don't like using the one in Xcode (and maybe I'm missing something obvious) because I got so used to the debugger in JS land where I get access to a live REPL which functions just like the code I write. In Xcode, I'm stuck with some lldb prompt which I don't understand and definitely doesn't function like the one in JS tooling. I'm sure it could be more useful if I invested more time into learning it, but the barrier is there.
in general, in lldb you can just do `e [swift or obj-c code]` and it will work like a REPL
I’ve used good debuggers in the past, but the main downside to me is that the workflow improvement is relatively minimal compared to print debugging. The “live programming” aspect of Common Lisp and Clojure, as well as the way Cider implements tracing for Clojure _is_ a major improvement, but only because they let you be more precise in what needs to be re-run for print debugging.
I think it's often that the compiler/environment do not leave enough information for the debugger, typically by optimizing out local variable names and their values. By the time you figure out the obscure settings to be able to see the live values of variables and other state, you may have done a lot more surgery on your build system and slowed things down to a crawl compared to adding a few print statements.
You forgot one: the debugger is auto-updated with the rest of the browser and is changing UI all the time.
Are there any good books on using debuggers effectively?
Not a book, but Andreas Zeller’s free “Software Debugging” course on Udacity is excellent: https://www.udacity.com/course/software-debugging--cs259
Everyone in the comments is talking about using this for their own debugging, however I think the way to win with this as a business is in two places QA and Automated QA. If you have real human QA people in your org and they could run this while doing QA. If they hit a bug, and could then share a simple link to the dev team that captured their issue + the stack.

Same goes with automated QA. Record the UI tests using this, and if one fails, store the state + stack.

There are a LOT of hard problems in that workflow... Good luck!

You're right on the mark here. I saw rr used at Mozilla to diagnose and fix flaky tests that failed just often enough in CI to make life miserable, but were nigh-impossible to reproduce in a local development environment. Being able to take that to the next level and collaboratively investigate a bug using a recording that captures it is game-changing technology from the future.

Imagine a world where instead of ignoring, skipping, or marking "known failure" on all those flaky tests your CI hits (we all have them) you could capture recordings of them, and then actually investigate and fix them! That world is possible!

Currently the only way to sign up for a personal account is through google. Is there another way that I am missing, or are there any plans to provide email based signups in the future?
(Replay engineer) Google is currently the only way unfortunately. We wanted to support secure logins at launch, with SSO and Multi-factor Authentication, and focus most of our efforts on the core product. As a result we just went with Google for launch.

We don't currently have any plans to add additional authentication mechanisms but we've heard this feedback from a couple folks and we'll sit down to prioritize it after the excitement of launch has died down. Sorry about that!

Even for Google logins, would you consider using a token-based authentication system (like Spotify, Postman, etc. do)... i.e., your default browser opens, Google logs you in there (or you already are), and that sends a backend auth token to your service to connect your Google and Replay accounts.

I would like to try your product, but am wary of typing in my Google credentials into an unknown, black-box browser. It's too easy to MITM, especially if someone redistributes a copy with a keylogger shimmed in.

The token-based auth means you can still log in with Google but never have to share your Google password with the proprietary Replay browser. Probably many of the multi-login vendors support something similar already if you don't want to deal with it yourself.

Thanks for the update. I had a feeling that this might have been the case due to the launch crunch and it's perfectly understandable. Really excited for the future of this project!
Congratulations! This looks super interesting!

How are side effects (mutating HTTP calls, cookie creation, other I/O) handled?

(Replay engineer): we basically record all the inputs and outputs to a program. In the example of an HTTP request: when recording we'd record that a request was made, and the response. When replaying, rather than make the HTTP request, we return the response that was recorded.

If you're interested you can learn more about how Replay works here: https://medium.com/replay-io/how-replay-works-5c9c29580c58

> Likewise, the raw pointer values don’t have an effect on the behavior of the JavaScript

There are actually some cases where raw pointer values can effect user-visible browser behavior indirectly. Notably if the pointer values affect iteration order in an associative data structure which the browser uses in some way that tickles JS differently depending on the order.

There are bunches of other bizarre edge cases you'd never think of as well that never come up (until inevitably one day they do). Another example: In old versions of Blink I've seen ligatures fail to form depending on what was in the font cache when the page was loaded.

(Replay employee) Yeah, later in the post (https://medium.com/replay-io/effective-determinism-54cc91f56...) we mention needing mitigations to ensure that hashtable iteration is deterministic in cases where it affects how the browser interacts with the JS engine. There are others needed as well to ensure that the browser behaves the same across a large range of websites, my favorite is handling sites that sniff information about the system's math libraries by e.g. calling Math.sin() on specific values and testing the results --- the values when replaying need to be bitwise identical with what happened while recording.
Ah I shouldn't have stopped reading. Then as one of the rare browser replay experts (there's gotta be at least like... 7 of us right?) I can certify your project as the real deal.
Thanks for the pointer. Will take some time to digest the content. Have you considered in making a library, so whoever wants a session replay can manually initiate a recording? So you don't have to maintain a browser fork. I am asking without fully understanding the technical detail. Please forgive my ignorance
From my reading of it it looks like it is language agnostic but only browsers right now. python and ruby support are in the works per their homepage
Thanks!
>nobody uses breakpoints

Lost me here, using breakpoints are an invaluable tool for people that actually take the time to use their tools correctly.

That's the point: looks like a tool made to be the best of both worlds: You get access to the full debugger, at any point in time, instead of clicking "next" a hundred times and then not being able to go back if you missed something. Here you go forward or back in time, and you can visualize where you are. This thread sums-up much better than me why people use print debugging (not just because they don't want to learn a tool, or are dumb), and why Replay is the best of both worlds: https://twitter.com/geoffreylitt/status/1438152748449636360?...
I'm inclined to agree - I use breakpoints all the time when I'm debugging JS code and it is most definitely an invaluable tool.

It can be a bit cumbersome to setup and it can be a little buggy especially when you're working with transpiled code, but to say no one uses breakpoints is a bit disingenuous.

Love that everyone can comment on the demos :) https://app.replay.io/recording/aae188fb-57d2-46af-a23a-0adb...

Thanks for making this. It's so cool!

I don't like that your marketing story normalizes not learning breakpoints. It's like the popular guy at school being self-deprecating and charismatic about being bad at math. You shouldn't position yourself as an alternative to breakpoints because you're going to alienate a lot of more experienced devs who are comfortable using a debugger.

Replay does look interesting. I'm wondering about an itch I can't yet scratch. If someone on my team knows how to repro, we don't really need Replay. I have repro steps and control of the environment. But if a customer in the field has a bug reproduceable that we can't reproduce in house, this is my itch. What is the least friction and least invasive way to get a Replay recording in that scenario?

Congrats on the launch! This looks awesome. I would love to see this for Ruby and other backend languages, I will definitely check it out at that point. I really like the unique approach, and I frequently struggle with breakpoints in rails because the call stack gets so deep. Being able to set a break point where the error occurs and step back through time would be a huge win.
Looks amazing, thank you for building it. Since React got mentioned several times on the landing page, i need to ask: is it framework agnostic?
(Replay employee here): We have experimental support for some react-specific features using react-devtools since React is such a common framework, but the Replay is a general debugging tool and will work with other frameworks well too.
Great. First saw one in Elm years ago, someone already mentioned prior art by Bret Victor though. Glad to see an agnostic tool for everyone.
Does it record network calls? We often build fronts for API'S so bugs are most of the time mismatch between frontend va backend expectations
(Replay engineer): Yes it does!
I just made an account did a recording but cannot seem to see the network request tab anywhere in the ui? I can make a brakepoint, add console.log statements and evaluate stuff (pretty cool tech) but where do I find network requests?
(Replay engineer here): We don't currently support inspecting network requests, but that is something we plan to support in the future.
What if the network calls depend on state in the browser and returned data depends on state at the server?

And what if you are debugging a race condition in network calls?

Replay deterministically replays the recording, so if the state of the application when you recorded it caused a network call, then when replaying it we will also "make" a network call _but_, instead of actually going out to the network we will instead return the exact data that was returned when you recorded it.

You can learn more about how Replay works here https://medium.com/replay-io/how-replay-works-5c9c29580c58

Can you expand more on what you mean by a race condition in network calls? If it's a series of network calls that the browser could make in any order than we will make them in the order that they occurred when you recorded it. If it's a race condition that occurs in your backend, then the Replay browser won't really help there (though it will show you the responses you got from your backend when you recorded it).

For help with that, you might want to use Replay on the backend. Right now we have Node support (https://github.com/RecordReplay/node), but other runtimes are on the roadmap.

Doesn't replit have a similar feature? Where you can link to different debugging states?

Are you aware of that? If yes how is it different?

(Replay engineer): Yes, replit has been working on adding similar functionality (https://blog.replit.com/debuggest).

In fact: we've been collaborating with them on it! :)

Looks great! I hope that you can make this work.
are there any public replays you can share so pple can get a feel of the product without downloading?
Yep! Tons on the site. Here's a Replay with an algolia search bug. Try adding a print statement in Hits.js!

https://app.replay.io/recording/cf2da81f-3a74-45ba-9241-f6d4...

Congrats Jason! Know you and the team have been working super hard on this
How does it work? Are you making memory snapshots? Something higher level?
Long story short, we forked the major browsers so that when you record we can capture the browser input (OS Library calls).

Brian walks through our approach here https://medium.com/replay-io/how-replay-works-5c9c29580c58

How will this work on the long run? Will you try to upstream the required changes or will the forks have to be maintained perpetually?
(Replay engineer): In the long run we'd love for Replay to prove to the major runtimes that they should build support for this in to the runtime itself, rather than us maintaining many forks. The API that we designed for recording a runtime is open source and available here https://replay.io/driver and could serve as a good starting point.
That’s gonna be a pipe dream unless an open source client appears. Consider releasing your tech as a self-hosted, turnkey solution and slap on some dual enterprise licensing.
Self-hosted is on the roadmap, but getting this to be a universal technique is definitely going to be hard, no arguments there. Gotta start somewhere though!
thank you!