What I cannot [re]create, I do not understand
Richard Feynman, one of the most brilliant scientists and physicists of our era, is the author of that line. His point was that, given a blank page and the knowledge he already possessed, he could take any theoretical finding and reconstruct it from scratch. To Feynman, that capacity was the real test of comprehension.
I’m a strong advocate of mastering the basics as well. A deep understanding of existing solutions to recurring issues is essential for anyone aiming to devise novel answers to fresh challenges. You must be familiar with every problem already solved in your discipline.
But there is a difficulty — where does one obtain such insight? Given the rapid pace of today’s technology landscape, those who build these tools rarely have time to craft detailed documentation that explores behind-the-scenes workings. So, what is the solution? Personally, I push for reverse-engineering.
I’m well-known for taking apart Angular piece by piece. That said, Angular isn’t the only framework whose internals I’ve studied. I’ve also examined Vue.js, Webpack, jQuery, and a host of other web libraries and frameworks. Right now, I’m digging into React. As a result, I believe I have gathered enough insights to share with you and kickstart your journey into reverse-engineering.
For me, reverse-engineering is akin to the wonder of uncovering something fresh. It’s that mindset of enthusiasm about discoveries, combined with a hacker’s curiosity—always probing further. My aim is that this guide will help you reach that same place mentally.
I’ve organized my insights into two parts. This initial piece covers the guidelines and foundational principles that guide my reverse-engineering work. The follow-up article provides a hands-on demonstration of these principles by reconstructing a small component of React from scratch. Additionally, it highlights several useful debugging tactics that can speed up your reverse-engineering process.
But first, let’s explore the reasons for undertaking this kind of work.
The WHY
Let’s be honest—reverse-engineering is tough. It’s demanding in terms of time and usually calls for a substantial background. So, why go through the trouble?
A common assumption is that the primary purpose of reverse-engineering is to deepen your expertise in a specific tool, potentially leading to a more attractive job offer. Given the short shelf life of modern tools, many figure it's not worthwhile to sink time into deep dives.
Certainly, reading through source code will give you a thorough command of any technology. Yet, that’s just one of many advantages you reap from this exercise.
As you navigate the code, you’ll bump into fresh design patterns for tackling everyday challenges—patterns you can later apply in your professional work. I have seen this happen in my own career numerous times. As an example, while reverse-engineering the Angular Router, I acquired skills in lazy-loading components and modules, which later enabled me to create a plugin-centric architecture.
You will also uncover unseen features of a particular programming language. While analyzing Angular, for instance, I encountered JavaScript’s monomorphism. Most technologies lean heavily on the platform’s core APIs, so you’ll get to know those as well. This process lets you discover new tools and see them in action, rather than solely reading theoretical descriptions and wondering where they fit.
Furthermore, if you opt to share what you’ve learned with others, you’ll boost your public visibility. It’s a mutually beneficial arrangement—assisting others also aids your own growth. My own breakthrough began with Angular-In-Depth (AiD) when I chose to document my explorations. From there, AiD evolved into the premier Angular publication, opened doors for me to speak at conferences, and led me to an ideal role at ag-Grid. When you devote effort to deeply understanding a technology, you also exhibit your ability to problem-solve, your persistence, and your inquisitiveness—traits that forward-thinking businesses seek in potential hires.
Reading code will feel increasingly natural as you practice, and exploring unfamiliar projects becomes easier. Robert C. Martin, also known as Uncle Bob, estimatess the ratio of time spent reading versus writing to be well over 10 to 1. Writing new code always involves reading existing code, which is exactly where every developer starts when joining an ongoing project. Reverse-engineering practice gives you a head start here.
So, reverse-engineering ultimately improves your overall engineering skills.
What you need to know before reverse-engineering
Let’s begin by examining the core knowledge that speeds up and simplifies reverse-engineering. Invest time in absorbing these topics. The amount can feel overwhelming, and you probably don't know it all yet. That’s fine. Dedicate an hour or two daily to focused learning, aiming to master these areas. Over time, you’ll get there.
A firm grasp of the underlying platform
Knowing the platform’s APIs thoroughly is the first step toward successful reverse-engineering. For web frameworks and libraries, the essentials are: JavaScript, DOM API, and browser API.
Knowing JavaScript well goes beyond being able to explain closures or how this binding works. It means familiarity with advanced concepts, including property descriptors (a technique in Vue.js), proxy objects, and bit masks (both used in Angular).
When it comes to the DOM and browser APIs, knowing how to add a node or schedule an async callback is just the beginning. You should know the outcome when re-appending an existing child node, and how browsers handle unknown elements. Also, dive into HTTP request APIs and their details, such as when exactly the fail callback gets triggered.
For JavaScript, check out Axel Rauschmayer’s books. For DOM and browser APIs, turn to MDN web docs and Google developers web updates. Reading specs directly is the ideal route: EcmaScript covers JavaScript, and WHATWG handles the browser environment.
DevTools mastery
Being fully comfortable with your browser’s Developer Tools is critical. I prefer Chrome. Inside Chrome, you should understand:
- what
$0refers to when typed into the console - how conditional breakpoints work
- how to pause execution on exceptions
- how to skip segments of code or exit the current function
- how to search loaded sources for specific text, and more
Google’s Tools for web developers remains the top resource for mastering Chrome DevTools.
Familiarity with design patterns and architecture
Common design patterns often appear in technology. Recognizing them brings practical benefits. For instance, Webpack uses asynchronous JS execution patterns found in the async library. Before ES modules, all frameworks and libraries shipped using the UMD format. As you work through more frameworks and libraries, spotting these repeated patterns becomes easier, speeding up your code navigation.
Technology-specific concepts
Knowing the concepts behind a framework or library is also beneficial. For instance, before dissecting a modern framework, you should understand what a component is. You might gather these concepts from official docs, detailed articles, or design documents. Explore all available materials before diving into the code, and keep learning about new concepts as you encounter them. I recommend resources that include concrete implementation details rather than highly simplified explanations for a general audience. Conference talks often lack the specificity you need. Instead, design documents on Github and advanced technical articles are far more useful.
Guidelines for exploring sources
Check a follow-up article to see how I applied these guidelines while examining React’s codebase. I recommend jotting down the insights you uncover as you progress. This way, you’ll eventually be able to connect everything and see the full picture.
Identify the part of a technology to focus on
The question I hear most often is about where to start—specifically, where to place the debugger statement in the code. You can derive that from your objective. Before you begin reverse-engineering, you should always define which aspect of the technology you aim to understand. For example, when I started reversing Angular or React, my initial focus was on understanding change detection. That was the specific area I targeted. Given what I knew about modern change detection, I recognized it involves syncing updates from a component instance to the DOM nodes. So my goal was to locate where these frameworks keep references to generated DOM nodes.
Think like a scientist
In my view, the scientific method—observation, forming hypotheses from those observations, and then experimentally testing them—is the best way to acquire knowledge. I apply this same model when reverse-engineering. These are the core steps:
- Observe and generate a hypothesis.
- Derive a prediction from that hypothesis.
- Test the prediction.
After you get the results, use them to craft new hypotheses and predictions. Keep iterating until you fully understand the target area.
Use inference to develop a hypothesis and a prediction. Inference means reaching a logical conclusion by combining observation and your existing background. For instance, if someone tastes an unfamiliar dish and makes a face, you infer they dislike it. Likewise, if a person slams a door, you might infer they are angry about something.
Beyond offering a systematic approach, I think assumptions and validations act as memory anchors. They help you retain what you’ve learned for a longer time and recall it when necessary.
Switch between debugging, exploring implementations in the sources and reading comments
Reverse-engineering isn’t purely about reading code. Actually, I devote only about 20% of my time to verifying a particular implementation detail in the source. Around 70% of the time goes into debugging a sample application. That’s why I’m convinced that solid debugging skills are essential for efficient reverse-engineering. The last 10% of the time, I read comments in the source or explanations of ideas found in the code. These comments often provide more insight than anything available online, so never treat them as irrelevant.
Use callstack to construct the application flow
As you continue debugging and place breakpoints at various spots in the code, routinely review the callstack. Looking at the sequence of invoked functions reveals the application’s flow. This approach frequently points you to the functions that contain the logic you’re searching for in the sources.
Don’t get discouraged by getting your hypothesis wrong
Expect most of your assumptions to be wrong—that’s entirely normal and even necessary. Sometimes this signals that you need to strengthen your foundational knowledge. But more often, it merely indicates that the framework or library uses patterns that are new to you.
That’s not to say you won’t feel frustrated. You certainly will. However, stay focused on your objective and push through the frustration. When you’re wrong, you’ve gained a new piece of understanding.
Allow yourself some time to think about what you’ve found
Barbara Oakley’s “A Mind for Numbers” describes two mental states we cycle between: focused and diffused. Both are vital when learning something unfamiliar. Focused mode tackles problems head-on with rational, sequential, and analytical thinking. Diffused mode, meanwhile, is where unexpected breakthroughs often happen, offering a “big-picture” view of a stubborn issue. It arises when you let your guard down and allow your thoughts to roam. So, resist the urge to grind for hours straight — take regular pauses to reflect on what you’ve found. Personally, I pace around my apartment, especially during creative tasks like writing, not just when dismantling code.
Kick off by grabbing sources and building a demo app
For reverse-engineering a framework, you’ll need its source code plus a small demo project using it. Since nearly all frameworks live on Github today, simply clone the repo. You’ll want to examine a specific version, so visit the “releases” tab and pick the newest one. After cloning, switch to that version using git checkout tags/[version].
Next, assemble your sample application. Keep it as minimal as you can — stay away from bundlers like webpack or the CLI tools that modern frameworks ship. In my experience, those just muddy the debugger’s waters. The most convenient setup is a single HTML file pulling the code from a CDN like unpkg.com. Only be sure the library’s CDN version aligns with the tag you checked out.
A note on serendipity
Chance undeniably influences results. When you re-trace the same code paths, a stray comment might suddenly click, or you’ll notice a revealingly named function you previously missed. I hit this all the time. That’s why I recommend stepping through the confusing part multiple times with the debugger, or revisiting it after a break. Don’t shelve it just because today it makes no sense — your next attempt may uncover fresh details, thanks to insights you’ve picked up in the meantime.
See all of this in practice?
Check out Practical application of reverse-engineering guidelines and principles.
