The key to learning deeply in programming

Today, I learned a new way to delve deeply into programming: having discussions. (Yes, I spent my entire Christmas learning programming because I had no friends and family around.)

My Christmas learning

Most programming education tools suck. They teach you how to do things, like a user manual, but don’t tell you where they come from and why they are needed. What can we do about it?

The Importance of Design Philosophy & Problem Context

The moment my friend explained the philosophy and historical context behind a technology’s development, everything clicked, and I learned the tool so much more easily. For instance, he taught me about the development of the app router in Next.js. He said, “Because the app gets very complicated over time, we need a better abstraction so that the team can work hand in hand without knowing what other people are doing.”

Okay, I understand what it means, but how does this philosophy show in the design of the tool? He pulled out the app he built and showed me the code with all the child components. “Look at the layout of my onboard page. There are so many tabs, but the onboard bar will stay the same no matter how the children inside change.” When I saw the code, I immediately understood why Next.js forced us to use “layout.tsx” as a name everywhere and how another engineer developing the children inside no longer needs to see any code related to the onboard bar.

After our talks, I learned that I can separate my programming learning into several layers.

Implementation level: How to debug my current problem right now?

  • This is what I did in the beginning and still do (when I’m lazy). I just copy and paste the error message and the code into ChatGPT or Stack Overflow. I try to run the code and see if it works.

Functional level: How does this function work?

  • This is what I’m gradually learning through having a technical learning template and forcing myself to stop and learn.

Philosophy level: How does this language work, and how is it designed? What problem is it trying to solve?

  • This is the part he taught me. With his experience, he gave me a conceptual view at the philosophical level and demonstrated it through his code template on a functional level.

Why do all these levels matter? It’s about patterns. Most engineers I meet don’t think about patterns or only have a “sense” but not an in-depth understanding of them. They just try to piece information together from their experience. In my previous work, when I asked a question, my ex-boss would just answer the exact question I asked but wouldn’t tell me what motivated this question and what the mental models I was lacking were. Over time, I wrote lots of code, memorized everything, and tried to apply it in similar cases, but I never truly understood it. Though My senior engineer tried to explain things to me, which I kind of got, it wasn’t as holistic and clear as when my friend explained things across three levels to me.

Another example is when I asked a front-end developer about React. I was in all the bugs and asked him how to fix them. Beyond debugging and thinking through the code, he taught me, “There is a difference between declarative vs. imperative language. Imperative language is what you know. You ask the computer to do step 1, step 2, and step 3, while declarative language, like React, is that you only want to know the beginning and the end and ask the code to show it when it happens.” All of a sudden, all these weird state management things in React started to make sense to me.

The last example he taught me was about the server component in the app router. Before, I thought it was cool, and more convenient than the way we did things before (something on the client side), but I never knew exactly why. And then he taught me the dream of Next.js. Rather than separating the front end and back end, what if we have one thing that can somehow remix them together, so we will never need to worry about connecting both of them anymore? I still don’t fully understand it, but I think it’s starting to give me a sense of why I might want to use it and an interest in starting my exploration. Because I thought the people who designed this were so confusing and stupid.

What’s the difference between a senior engineer and a junior engineer?

Asking the right question.

It may sound super easy, but it’s hard to apply it. For instance, the last time when my audio file didn’t play, we debugged it for two hours, and he said, “Wait, it’s because QuickTime Player doesn’t support MP3!” For me, why is it possible for an audio player not to support mp3? Isn’t supporting MP3 the most basic thing for an audio player on this planet? However, without challenging the assumption that “My audio player might not support the current audio format,” I don’t think I would ever figure out this problem even if you give me a century.

Another example is the getstaticprops the function I tried to learn in Next.js. I was annoyed by why this function disappeared in their new version, App Router. I thought, "This is so stupid, is it because this tool is so new that they haven't built it?" He immediately caught this assumption and asked, "Why is it not there? What might be the reason behind it?" And then we learned why App Router exists.

Today, we spent three hours together just talking through the project I worked on yesterday. What were my assumptions when solving the problems, deciding the tools to use, and how do form all my decisions?

In essence, when he keeps asking, “What do you assume about it and why?” it helps me catch the wrong assumptions and accumulate a better understanding to ask the right questions. However, how to ask good questions then?

How to ask good questions?

From him, I learned these two questions helped me a lot with the process of actually understanding it.

  • Why? Why doesn’t it work? What is my hypothesis when I see my audio files don’t work? When I exclude some possibilities, what’s my assumption behind it?
  • How? How do you utilize the concept you just learned in the code you have now? For example, how do you pass information in the event handler without having it in your component?
  • So what? What will happen if we introduce this new function and tool? What kind of difference will be made? For instance, if the server component is implemented, what will it look like for the user?

Throughout our conversation, I finally learned what it means to delve deep into something. People often say that to become proficient in one thing, you should keep asking why and focus on learning one thing deeply. I know this, and I think everyone knows this, but none of us knows how to apply it when we are new to something. I used to make lots of mistakes by looking into irrelevant code because I was trying to go deep and felt frustrated wondering why I was just getting more confused. I’m super grateful that today he showed me a pathway on how to do it effectively and became my free tutor along the way.

As I have enough knowledge on implementation and functional level, the next step I will take is to learn all the design philosophies and problem contexts of new technologies. I believe it will give me better intuition into asking the right questions as a junior engineer.

--

--