DEV Community

Jordan Los for Jobber

Posted on

Interpreted Languages: Reading Source Code

Types of Reading

In my previous article, Serious Jest: Making Sense of Hoisting, I noted the Jest docs say they hoist their mocks. Hoisting means the Javascript engine assigns references in memory to some objects before executing code. But for Jest, hoisting is executing mock statements before import statements. So even if you define mocks after your imports, they will execute first. That Jest uses hoist to mean something different is not at all obvious and why I wrote the article.

After posting, I was asked how I made the connection. The short answer is I spent a bunch of time stepping through Jest source code in my debugger. The longer answer is I followed a process I picked up in the humanities. Reading Plutarch's Parallel Lives may not seem like reading source code, but you have the same goal. You read to understand the intentions and ideas of another person.

How To Read a Book by Mortimer J. Adler and Charles Van Doren best describes the reading process. They describe four types of reading: elementary, inspectional, analytical, and syntoptical. Each type of reading played a crucial role in researching and writing the last article. I’ve structured this article around them.

Elementary Reading

Elementary Reading is being able to understand the grammar and syntax of a language. This level of reading is the prerequisite to understanding another person's ideas. There's not much to discuss at this level of reading for our purposes.

Inspectional Reading

Inspectional reading is strategic skimming. You want to grasp, as quickly as possible, the structure and meaning of a work. The sooner you learn the structure of a work, the sooner you can grasp its finer points. This means skimming the chapters, watching the movie first, or listening to a podcast.

The same principles apply to code. Skim docs, look at function names, and read short articles. By the end, you should have familiarity with key concepts, files, and docs. I read blogs on hoisting, browsed the Jest source code on Github, and watched a few youtube videos. I identified two critical areas while inspectional reading: the ECMAScript spec and a few key files in the Jest repo. I also had notes on scopes, closures, and the Javascript Execution Context.

Analytical reading

Analytical reading means reading for intent, structure, and relations. Different genres have different rules for these things as does code. Some parts of a file are "the main point" while others serve a supporting purpose. Since analytical reading is the most time-consuming and fruitful, prefer primary sources. Formal specs and source files are harder to read than summaries. But source docs give you unfiltered detail at a granular level. How do you know that blog post didn’t miss something relevant to your problem? If you haven’t read specs and source files, you’re trusting someone else’s interpretation.

I started with the ECMAScript spec section 9. It didn't have any direct answers, but I had a better mental model of what was happening when my tests ran. The ECMAScript spec made sense of the Closure section of the Scope tab in Chrome DevTools. Monitoring the closures section during debugging helped answer several of my questions.

Setting up the Debugger

  1. Run node --inspect node_modules/.bin/jest --runInBand path/to/your/test1

  2. Open up Chrome and go to chrome://inspectand click Open dedicated DevTools for Node.

  3. Open up your test file in Sources tab and run your tests

Using the Sources Tab, I executed the test line-by-line. I did this for test files, the components under test, and some Jest source code. The key Jest file I looked at was node-modules/jest-runtime/build/index.js. At each step, I monitored the Closure section and paid attention to the mocks. I monitored when the mocks received references and documented those changes. I then followed this line-by-line process for each of the four examples. Following code execution, while writing down my thoughts yielded the most results. I wrote out what I thought was happening, noted my questions, and made sure to answer every one of them.

This stage of analytical reading is when I figured out:

  • Jest alters a test file's execution order. Mocks execute before imports. This is Jest hoisting. Javascript hoisting happens before execution.

  • Jest collects it and describe blocks but then executes them later

  • When Jest was actually evaluating function expressions defined within the test files

When doing analytical reading across several works, you produce a lot of notes. Organizing those notes into something coherent is the next step.

Syntoptical Reading

Syntoptical reading is a term coined by Adler to mean reading across many authors. If analytical reading is conversing with a single author answering their own question, then syntoptical reading is conversing with many authors to answer your own questions. You then take your answers and synthesize them into a cohesive line of thought.

I wanted to know how Jest and Javascript handled different ways of creating mocks. Bringing notes from many sources together is a fuzzy process. I’d look at a note, get a question, and run through the debugger again. Or I'd peek at the ECMAScript spec. Or I'd go look at a couple of blogs to work out things like the Temporal Dead Zone.

Reading your notes across many works is when you make novel connections. I realized here that Jest executing a mock meant something different than I assumed. If the mock is executing, then the Javascript engine is operating in the execution phase. And if Jest hoists during execution, then it's not the same type of hoisting as in Javascript. This only became clear, however, after all the previous exploration.

A good sign the end is near is when you can create a rough outline from your notes. Here was one of my earlier outlines:

  1. Sneaky Temporal Deadzone
  2. Test Step Through
    1. Import parent module
    2. Jest function: requireModuleOrMock
    3. Jest function: requireMock
    4. Critical line of Jest code: node-modules/jest-runtime/build/index.js Line 802: const module = this._mockFactories.get(moduleID)()
  3. ECMA Script Sepc
  4. Function Declaration vs Function Definition
  5. Jest Docs

This outline differs from the one I finally published. I still had questions and every time I answered one I saw a better way to organize my thoughts. The final iteration came after I had answered my questions on how the 4 examples were executed. When I put that final set of notes together, I could see there were 6 stages that each example went through. Those stages explained the differences in how the examples were executed. From that point, the rest of the article was filling in details from my exploration.

Conclusion

Code describes machine instructions which distinguishes it from other textual works. Despite its mechanical nature, source code still embeds ideas in human-readable language. So reading to grasp ideas in code will use the four types of reading I've mentioned. By understanding each type of reading, you can focus your intent when doing your research. So dig into that source code and make sense of your problem. All the rest is commentary.

About Jobber

Our awesome Jobber technology teams span across Payments, Infrastructure, AI/ML, Business Workflows & Communications. We work on cutting edge & modern tech stacks using React, React Native, Ruby on Rails, & GraphQL.
If you want to be a part of a collaborative work culture, help small home service businesses scale and create a positive impact on our communities, then visit our careers site to learn more!


  1. Debugger explanation: 

    • inspect allows you to access the test in your chrome dev tools

    • runInBand prevents Jest from running in parallel (not great for debugging)

    • running Jest from the node_modules directory was necessary in my environment to properly connect to the node debugger. This might vary depending on your environment.

Top comments (0)