A Lifetime of Learning to Code

Transforming “Minimum Viable Developer” to Lifelong Student

I teach computer programming (specifically web development) at a so called coding bootcamp, Galvanize, in San Francisco. I have a BS in computer science and I’ve been teaching in one form or another for my entire life.

Coming from a computer science background, my philosophy about what to teach is somewhat controversial within the bootcamp community.

For example, I truly believe that so-called theoretical skills such as Big-O notation and memory allocation are important in the day to day life of a software engineer. The alternate viewpoint is that these skills are competing for precious time with more pragmatic skills that can be applied to the job on day one. The rallying cry of many bootcamps has been that the traditional path to computer programming is a broken one; that these theoretical skills are not the ones a modern programmers needs.

The shortened timeline of the bootcamp educational model further muddles this issue.

The promise of the bootcamp industry is nothing less than “job readiness in 3–6 months,” which means every hour spent on frivolous topics is a significant lost opportunity. We are teaching beginners whose goal is to start a professional career in computer programming. Because bootcamp students don’t have the luxury of 4 years to truly survey the enormous field of computer programming most of these courses target a specialty; Galvanize has two targeted immersive courses: Web Development and Data Science.

Faced with this shortened timeline and the ambitious promise of job readiness, many people in my industry over-correct academia’s relative sloth by asking: “What is the minimum viable developer?” or “what is the minimum a student must know to go to market”?

These are natural questions to ask during the pinnacle of the Lean Startup mentality. Following this “pragmatic” philosophy bootcamp operators build their curriculum directly from market research. The approach looks like this:

We need to teach students skills matching common job listings. So we looked at 1000 job listings in a growing market, found the most common requirements, and designed a program tracking closely with these skills and technologies.

It’s not surprising that the resulting curriculum looks like a job listing itself — and this does have its advantages.

This kind of curriculum gives recent graduates a sense of confidence that they learned marketable skills. It gives prospective students a clear sign that the program is tracking the marketplace. In an industry that is known for constant change, this is a positive signal indeed.

When the minimum viable developer philosophy is taken to the extreme students can get a job without enough fundamentals to thrive later in their career.

By focusing on what is current, many bootcamps are neglecting to teach the things that are permanent. Said another way, the bootcamp approach necessarily ignores a very common job requirement: “BS in Computer Science or related field.”

This balance between perennial theory and concrete technologies is on my mind constantly.

As principal instructor at our SF campus I am one strong voice in the development and enactment of curriculum at Galvanize. One of the common criticisms of bootcamp is that graduates lack a strong engineering foundation. Self taught engineers often find themselves in a similar position.

Regardless of your background or how you got into programming, a deeper understanding of the inner workings of computer programs is going to: extend the length of your career in programming; increase the number of jobs for which you are eligible; and improve your earning potential overall.

I wish to set out a topic list for the life-long programming student. The topics I have selected are what I view as the connective tissue of programming; the topics that — once mastered — give a programmer a leg up in every language and framework. These are topics that are low level enough to be widely (some universally) applicable, but high level enough that their impact can be seen in your daily work.

6 months is nowhere near enough time to master all of these topics. It takes several years of hard work to become a master of anything, and programming is no different.

I also want to impress upon new programmers the goal of being a “master generalist.” Someone who responds to all problems by saying “I don’t know how to do [task] right now, but I know how to find out.” Familiarity with these topics will prepare you to succeed at any software engineering task. With that in mind, let me take a moment and share my thoughts on what it means to be a high quality junior engineer, and how to approach your time as a junior engineer.

The Role of Inexperienced Programmers

Typically, your first few jobs will offer you more opportunities to learn than your subsequent jobs. Try to get the most out of these early years. If you can, champion a culture of learning and help your company institute a learning focused policy like: a stipend for educational products, or dedicated office time to explore new topics.

Once you develop a specific skill set it’s easier to get pigeon-holed into one industry or technology. Rising in the ranks of your company means your bosses will want you to spend more time producing software efficiently. Getting pinned as “The Front End Person” or “The Java Person” early on can get in the way of crucial professional exploration.

To understand why you have more freedom to learn in your first years, pretend for a moment you are a hiring manager for a software company, TylerCo.

TylerCo has grown into relative stability, some of the brightest engineers are being deservedly promoted, and you’re hiring junior engineers onto their new teams. What are you looking for in these humans? What are the 3 most important qualities of these junior engineers, to you?

Take a moment to think about it. Here is my list:

  • Knows any 1 programming language well.
  • Autonomous learner / has a growth mindset.
  • Can read and debug code.

I don’t really expect these junior developers to move the needle on day one. I don’t mind if they don’t already know the specific technologies in our stack, provided they can learn quickly. I already have an awesome team who got TylerCo this far, and they’ll probably continue to perform. These junior engineers are going to bring fresh ideas, but ultimately I know they’ll produce work slower than my seasoned experts.

In the short term, TylerCo is really trying to create leadership, mentoring, and teaching opportunities for the newly minted managers. I want to hire adaptable, flexible, generalists — so that they can learn the philosophy of “Coding At TylerCo™”.

My team will train them on the technology specifics, so long as my new hires have the proper foundation and attitude to learn on the job. It’s especially important to me that they can do much of this learning autonomously, so their ability to read the existing codebase is critical.

Companies invest in less experienced engineers intentionally — TylerCo could simply open mid-level roles if we needed experienced engineers now. To make this pay off, our hires need big learning potential. Hiring skilled learners gives my experienced programmers the best chance to have positive teaching and mentorship experiences. It also means that (hopefully) instead of hiring mid-level engineers in a year or two we can promote people who were trained in the TylerCo style and already know the system. As hiring manager I’m willing to sacrifice on specific skills in favor of aptitude because I see these new engineers staying with TylerCo long enough to develop into great engineers who already have Tribal Knowledge.

If my new hires are autonomous learners who can read code, they will always say “I can figure out what’s going on here!” But even the best autonomous learners can get stuck when they don’t know the lingua franca. This is where asking “what is the minimum viable developer?” breaks down a bit. If my engineers only know the React nomenclature, not general programming nomenclature, they are much more likely to get stuck on a problem which isn’t specific to React. To combat this problem, students need the vocabulary to be able to research any question from any technology stack.

Learning from tutorials, online examples, and documentation is much easier when you already have some domain specific knowledge. Similarly, understanding some of these foundational principles can help a programmer make sense of an unfamiliar program.

Autonomy in learning is the first skill a programmer needs if they want a long career. The second is business savvy.

The Business Savvy

My own ~6 years as a professional programmer does not yet constitute a “long career,” so I’ve tried to learn from others. I’ve supplemented my personal experience with research. Classics like The Mythical Man-Month, The Design of Everyday Things and The Pragmatic Programmer influence my opinions here. Modern wisdom about technical skills can also be applied to the business aspect of programming. Such overlap can be found in 97 things all programmers should know; as well as conference talks; and the blogs of other programmers, professionals, and companies. For example, two of my favorite blog posts demonstrate this overlap: On Being Senior, and Choose Boring Technology.

The stories and advice I have received from my own managers and mentors has deeply influenced how I think about careers as well. For example, my manager gave me this crucial feedback shortly after I started my first programming job: “You suck at negotiating your salary.” Which has probably turned out to be more valuable than any of the technical skills I have studied in terms of financial outcome.

For advice on negotiation, try this podcast.

Programmers need to take a keen interest in the business aspect of their career. Be ready to explain why you are valuable in terms of your employer’s bottom line. If you can’t describe why someone should pay you, they probably won’t.

As an example, the ability to estimate the value of a potential feature in terms of revenue is a fantastic business focused skill that all professional programmers should develop outside the realm of the technical. Consider using Fermi Estimation for this.

Flexibility and Adaptability

A common theme in programmers with long careers is an eventual pivot. To architecture, or management, or to start a business, or to get out of the “software industry” and use software as a secret weapon in another field — teaching in my case. The trajectory is pretty wide, but even before you hit “Senior Software Engineer” your options get pretty interesting.

I once read about some engineers who quit their day jobs to make a living using machine learning to absolutely destroy online fantasy sports leagues — it’s not “Software as a Service”, but the money is just as green.

Here’s a paper published by Stanford that specifically mentions DraftKings and FanDuel. You’re welcome.

The world of software is going to get a lot bigger as access to computers of all kinds expands. The only constant in this business is change, and we’re seeing diversity of technology increase dramatically. We are still just seeing the tip of the iceberg in computing. Which brings me to…

The Technical Skills

To have a long career, programmers will likely need the skills to switch industries at least once. To prepare for this, programmers should know the lay of the land, and have the skills to travel around within the landscape. Moving around within any given software organization is especially important for beginners because the first job you get probably won’t be a perfect fit.

“Software” is an extraordinarily broad field. Consider this shortlist of industries: web development, game development, super-computing, security, data science, robotics, virtual reality, computer graphics, computer vision, natural language processing, artificial intelligence … the list goes on.

Technologies will come and go; being able to jump ship when your industry starts to wane (or worse, its bubble bursts) is important. Besides, pursuing whatever interests you most is infinitely more satisfying than pursuing only what you started with.

One of the de facto qualities of a beginner is that they don’t yet know the whole landscape. Skills that travel across industries and connect programmers in multiple fields should be valued above single-application skills; pigeon-holing oneself should be avoided.

Programmers need to be adaptable enough to apply to jobs for they don’t meet all the “requirements” on the list.

If I only got job offers for which I had every bullet point on the requirements list … well, I would never have had any job offers at all, quite honestly.

To make this point more concrete: I once earned an interview with AirBnB partly on the strength of my cover letter, which starts by stating flatly and honestly that I do not have the experience they are looking for:

Hello Persons of Airbnb,
I am an experienced full stack engineer; world traveler; satisfied Airbnb user; and I’m interested in this position at your lovely organization. The requirements, such as they are, are quite suitable to my background:
“Ruby-on-Rails in production environments”
No. But, I do I have solid web fundamentals as well as experience in Python+Django and Node.js.

Requirements are all secretly nice-to-have lists; companies frequently hire programmers whose resume does not “fit” the requirements. A long career means constantly learning on the job, and hiring managers generally understand this fact.

I strongly encourage students to explore many fields. Learning and using several different disciplines comes with a certain gestalt; eventually it becomes intuitive to read code and identify bugs. As a programmer extends their skill-set, their understanding of how everything works together becomes stronger than their understanding of any one individual part.

This big picture view is what makes it easy for experienced programmers to get started at new companies and on new projects quickly. Instead of constantly learning new specific technologies from scratch, great programmers have an intuitive understanding of how things work generally, and apply that knowledge to quickly understand a specific situation without a tutorial or in-service.

What’s Old Becomes New

While modern computer programs are some of the most complex things known to humans, the foundations of these programs are found in ancient history. Devices like The Antikythera Mechanism utilize similar patterns of thought. The more modern slide rule applies common principles of Computing. TCP — a very important internet protocol — may have been independently created by ants, probably millions of years ago.

Targeting more universally found patterns gives students a broad “knowledge horizon” (the “known unknowns” in your own understanding) which guides the programmers research into novel problems.

Whatever specific skills you’ve already developed, the deeper principles should be explored. Ideally the specific skills should be learned in the service of exposing more universal patterns. Learning quickly usually means connecting new information to an existing concept in your mind. This is part of why analogies are such powerful learning tools.

Computer programmers are all standing on the shoulders of giants; If you’re starting to program in 2017 so much has been built for you. From programs on punched cards, to assembly language, to compilers, through the ARPANET to The Internet… Learning all the details of all these systems is the study of a lifetime, but having a somewhat longer view into the history of programming often helps explain why programs work the way they do.

Understanding the following topics can give programmers an anchor and a torch in any program, regardless of language or framework.

Memory

Beginners should learn how computer memory works right away; starting with The Stack and The Heap. They should understand the concept of Passing by Reference as soon as possible as well. These three key principles can help a programmer debug common side effect bugs. They also give the programmer a helpful a mental model of how code executes.

Aspiring masters should learn to use pointers and eventually write an implementation of malloc in a language like C. They should also manually allocate data on The Stack by writing some programs in assembly language.

These low level details are not always “marketable,” in exactly the same way the latest JavaScript framework is, but they are worth it. No job listing has the bullet point “must implement a function to recursively compute the nth digit of the Fibonacci sequence in MIPS assembly language,” — but doing exactly that was one of the best things I’ve ever done for my career.

Implementing recursion at the level of assembly language dramatically changed the way I think about how computers, memory, bits, and the physical transistors are connected. It clarified scope, it solidified memory allocation, pointers and reference types were suddenly so obvious.

My deeper understanding of these things has certainly improved both my performance in interview situations, and on the job.

Speed

Understanding what makes a computer program fast or slow should also be a priority because it explains why so many things are done. Concurrency is tough, caching is tricky, why do programmers even bother with all this confusing architecture?

The complexity of computer systems are often due to their hardware’s physical limitations. We create software solutions to squeeze the maximum performance out of our hardware — compression is a straightforward example of the ever common trade-off between processing power and memory. Future masters of programming should learn about complexity theory, the slowness of I/O, and how to write timing code.

Concurrency and Parallelism are hard to avoid in some fields. The web and JavaScript are a great example of this. Concurrency specifically is impossible to avoid in any modern front-end web application — between AJAX and event handling you can easily create race conditions without realizing it. Understanding concurrency is a crucial debugging skill for web developers. Parallelism is easier to avoid for now, but not forever; the aspiring master should absolutely use something like Pthreads in C, or experiment with GPU parallelism using CUDA for example.

Eventually, the role of physical infrastructure should be examined. Concepts like cache behavior, branch prediction, instruction pipelining, logic gates, and memory access times should be explored. Not because you want to design chips for IBM, but because you want to get the most out of your hardware.

Paradigms

Object Oriented Programming (OOP) is just too common to ignore. Programmers should understand classes and objects as instances of classes. Programmers should learn a statically typed object oriented language like Java or C++ early on. Especially if you start with something pretty permissive like JavaScript, Ruby, or Python.

Understanding the concepts of state, behavior, and identity in objects will help developers immensely. Inheritance, polymorphism, and encapsulation should all be explored relatively early on, especially if you’re interested in companies with larger (and older) codebases like Google or Microsoft.

Functional Programming is another core paradigm to learn. It contrasts with OOP in many ways and is strong where OOP is weak. I was once told this gem about the zen of programming:

Some say that an object is simply a poor man’s closure, others say that a closure is just a poor man’s object. Wisdom is to understand that both are correct.

State and behavior are nice and all, but in large (especially parallel) systems they can often be a source of pain and unpredictability. Functional programming can be used to avoid some of the headaches of unpredictable state.

Closures show up in popular languages like JavaScript and Python commonly. Understanding them will lead to a deeper understanding of scope and strengthen our notion of The Stack and The Heap. Being able to rewrite loops as recursive functions, and writing pure functions can further reduce messy states, and make your code thread safe which relates back to concurrency and parallelism. Just as programmers should explore something heavy-handed at OOP (like Java) programmers should experiment with a serious functional language, like Haskell.

Concepts of declarative programming outside functional programming — especially SQL — should be explored as well. There are lots of competitors in the database space right now, but I believe that relational databases are too embedded in the current world to ignore: Stack Overflow reports that roughly 50% of all jobs use SQL, only JavaScript is more common.

A programmer should absolutely learn to make tables and write queries in a SQL database. Go deep enough to write queries with joins and aggregates. Understand Foreign Key relationships completely, they will strengthen other areas of interest like pointers and passing-by-reference. Learning what makes a database efficient is enlightening as well; indexing and normalization should be examined by the aspiring master.

Data Structures

Some data structures appear over and over again in computer programming. Many of these structures are intuitive enough that novice programmers can (and do) conceive of them organically; programmers encounter a problem that is solved effectively by using key/value pairs sometimes end up inventing a Map.

Many of these structure are so crucial to programming that they are baked into programming languages. Arrays are perhaps the best example of this (quick name a programming language that doesn’t have an Array type!)

A knowledge of data structures, and common algorithms that apply to these structures, can save programmers from reinventing the wheel. It will also help us know when to use which built-in components.

Fundamental data structures are:

Each of these data structures has strengths and weaknesses, and each of them can be found across all programming industries. Understanding these structures gives programmers a bigger tool kit; a set of mental models that can be adapted to almost any programming problem.

Understanding these well-studied data structures can help programmers know when to use a library, and when they need to build something from scratch. Programmers who don’t have data structures in their repertoire will find themselves solving problems “the hard way” every time they encounter a new problem.

Programmers experienced with data structures and algorithms will “reduce” a novel problem to a well known data structure, and therefore will be able to apply a well known solution. This is one way that new programmers can stand on the shoulders of giants to reach great heights faster and with significantly less personal effort.

By the way if you want to practice working with Hash Functions and Hash Tables I built this github repo to help you practice and learn more.

Educational Dividends

Memory, speed, and data structures were all just as relevant in the design of the first operating system as they are in building modern large scale web platforms. You do not absolutely need to know these things in order to build a modern application, but the fact that these concepts are ubiquitous throughout all programming languages and paradigms should at least give you pause.

My own exploration of these lower level details continuously pays educational dividends that help my knowledge to grow much faster. No matter how confused I am about some new program I can fallback on these universal concepts to guide me in an unknown codebase.

Like any investment, we make a payment now in the hopes that our knowledge will turn into more knowledge later. These dividends pay into specific skills long after the original investment was made. Even if you only implement one recursive assembly program, your understanding of the stack frame is forever expanded. You level up faster in debugging, finding memory leaks, and thinking programmatically henceforth. Investing early is like a 401K: the earlier you start investing, the larger the payoff overall.

Programmers need to optimize for both future learning speed and retention — which means we should target skills that can be used regularly in addition to catalyzing epiphanies that pay dividends into these skills. For example: knowing about The Stack, The Heap, and especially understanding “reference types” pays dividends into the skill of debugging, but we still need to be debugging regularly to get the benefits. Programmers will debug code every single day, which is why creating a mental model for memory allocation is one of the first things I teach.

General Via Specific

Opponents of Computer Science material often say something like,

If I teach my students merge-sort, they probably won’t retain that knowledge, because they’re never going to implement merge-sort from scratch outside of this classroom. They’re going to use [hot new web framework] right away. We have to teach authentic skills that they can really apply.

And they have a point. This opinion is especially correct if students simply memorize the steps of a few sorting algorithms, which (sadly) happens quite frequently.

Instead, I use merge-sort as an example to teach students the underpinnings of complexity, Big O, and performance. Many sorting algorithms are taught in order to reveal the tradeoffs between memory use and CPU consumption. If a graduate can identify problems better solved by insertion-sort than quick-sort, then having implemented these algorithms is going to pay dividends into the general skill of writing high performing code. If they learn how making assumptions about input data can improve speed (using sorted data to perform binary search for example), they’ll be better at designing data models forever.

If students can explain why combining quick sort and insertion sort can lead to better overall performance, they’ll better understand some of the limitations of Big O and the difference between working with small and big data.

Programmers who have Big-O on their tool belts can analyze any block of code using Big-O. Programmers with the ability to write benchmarks can similarly time any arbitrary block of code. These skills pay big dividends into critical “senior qualities,” like performance optimization.

Balance and Reality

For many people, the educational investment metaphor is also a very literal reality. Students spend time and money on an education because they hope these skills will lead them to a satisfying and lucrative career. In delivering that promise to prospective students, we have to cater to the current job market to some degree. I don’t think we should go so far as:

“I’m teaching my students React because there are a billion React jobs.”

This helps with tracking the market, and obviously speaks to this issue of retention and frequent use. That said, I do not believe that learning something like Angular or React as a standalone concept pays big dividends. But the tactic of teaching general concepts through specific applications extends to such specific technologies as well.

Knowing Angular or React is a highly valuable skill for now, but in 5 years it may not be so valuable — but the philosophical underpinnings of these frameworks (Model View Controller and Component Based Design) have been ever present in the history of computing. Programmers will always be acquiring some temporary skills like the new hot framework — having the foundations to learn the next one quickly is the true skill. If you’re going to teach a modern web framework you should do so in service of giving students a deeper understanding the underlying concepts, killing two birds with one stone.

With the right perspective, students can expand their understanding of trees and Object Oriented Programming via The DOM. AJAX and event handling can be used to understand concurrency. If you’re teaching server frameworks like Express as an example of the “Model View Controller” design pattern then your students will cash in their dividend when they learn Flask, Rails, Django or whatever else is popular.

Recall my AirBnb cover letter — that wasn’t a fluke. The technical recruiter was ready to accept that Django was an acceptable replacement for Ruby-on-Rails, largely because I can explain why it’s relevant experience even if I don’t know much about Rails.

It’s important that we use specific examples to motivate the understanding of general concepts. Teaching people abstractions directly is extremely challenging. Targeting general concepts via specific examples is a much more powerful tactic, but it requires us to go deeper on individual topics. This depth is worth the investment, even at the cost of leaving other topics for later. The reason working with Python/Django is relevant experience for a Ruby-on-Rails job is that both of these two languages and frameworks are rooted in the same patterns.

Rooted in Patterns

Learning new things is easiest when you can relate it to something you already understand. Nearly all programming languages have constructs for control flow, functions, variables, and data-types. This is why programmers who already speak Ruby and JavaScript have an easy time with another dynamically typed scripting language like Python.

In fact, if you speak Ruby and JavaScript pretty well, you kind of already speak Python. In addition to both Python and Ruby being dynamically typed scripting languages, Django and Rails are both based on the “Model View Controller” (or MVC) design pattern. Knowing these abstractions can help us travel from language to language or framework to framework with very little resistance.

As an example I will present two snippets of code with very deep similarities. Both examples use components of the two common programming paradigms — Object Oriented Programming and Functional Programming — both languages have the same “weakness” in their class/object design (lack of encapsulation), and in both examples we work around this weakness by creating encapsulation using a closure, first in Javascript:

function Cat(age, name) {
var age = age; // Private
this.name = name; // Public

this.askAge = function(askerIsTrustworthy) {
if(askerIsTrustworthy) {
return age;
}
}
}

var chunky = new Cat(10, 'chunky');

console.log(chunky.name); // chunky
console.log(chunky.age); // undefined
console.log(chunky.askAge(false)); // undefined
console.log(chunky.askAge(true)); // 10

In Python

def make_cat(age_in, name):
age = age_in # Private

class Cat:
def __init__(self, name):
self.name = name # Public

def ask_age(self, asker_is_trustworthy):
if asker_is_trustworthy:
return age

return Cat(name)

chunky = make_cat(10, 'chunky')

print(chunky.name) # chunky
print(chunky.ask_age(False)) # None
print(chunky.ask_age(True)) # 10
print(chunky.age) # Error

If you speak Python or JavaScript it’s a lot easier to orient yourself into the other example. If you speak Ruby reasonably well you can probably read the Python just fine and use that to understand the JavaScript.

If you don’t speak any dynamically typed scripting languages, but do speak something like Java, C++ or another Object Oriented language, you can attach to the class keyword in the Python code and start working outward from there to understand the rest. If you’re familiar with Functional Programming you can see that both use a closure to make our Cat’s age “private”.

If you don’t understand any of these general principles then you may have to understand this code from scratch — and that’s a lot harder.

That isn’t to say domain specific knowledge isn’t important. For example: a seasoned Python developer would probably just use a convention instead of creating a closure to mark the age variable as private:

class Cat:
def __init__(self, age_in, name):
self._age = age # Private
self.name = name # Public

def ask_name(self, asker_is_trustworthy):
if asker_is_trustworthy:
return self._age

chunky = Cat(10, 'chunky')
print(chunky.name) # chunky
print(chunky._age) # 10

Inexperienced programmers should learn why some things are done by convention, but avoid simply memorizing lists of conventions. These help us answer questions like, what trade-offs are we making when our team agrees to use an underscore to mean “private data”? This is part of the history of programming, and modern programming languages borrow from this lingering zeitgeist. For example, the capitalized ‘C’ in Cat identifies it as a class in both examples. This convention is so common that not following it triggers compiler warnings in some languages and linting tools.

Like grammar in spoken language, the basic constructs of programming do not change wildly from language to language or over time. In spoken language there are verbs, adjectives, and nouns; we conjugate; there are tenses. In programming there are functions, variables, loops, conditionals, and data-types. In both programming and linguistics — there are more shared concepts than there are concepts which are unique to one language.

In Conclusion

I believe that students should begin their education with the fundamentals. 3–6 months might be enough to make students a competent React developer, but that knowledge will atrophy quickly if their competency wasn’t in service of something deeper.

Peter Norvig reminds us that it takes more like 10 years to really learn to program. Matt Might has quite a list of things “all” computer science students should know. Both of these articles overlap with the “97 things” collection of essays linked above. But don’t furrow your brow and say “If that’s true, I’ll never be a programmer!” instead embrace these lists as aspirational.

We all start our careers knowing relatively little. Hopefully we finish them knowing quite a lot more.

Brilliant and respected masters are learning new things every day; even the most skilled programmer started their careers by learning to print “hello world”. Your future mastery may be further down the road, but every meter you travel unlocks new super powers.

On this long and winding road, no one should skimp on the fundamentals — they are the tools that lengthen your stride.