Humanity mastered mentorships and apprenticeships many centuries ago. Programming requires nothing more than a cheap laptop and internet access. Yet, learning to code is still considered difficult, why?

Modern education has mostly ditched the centuries old idea of an apprenticeship, to the detriment of students.
Modern education has mostly ditched the centuries old idea of an apprenticeship, to the detriment of students. Image credit: Libray of Congress.

An Envied Field

Computer Science is a practical science that can be practiced with very few risky consequences. Think about the plethora of practical sciences that exist, and consider how you practice them. If you are studying Medicine, you don’t learn by attempting to diagnose random people in the street. An aspiring Nuclear Physicist cannot set up a mock nuclear reaction at home without joining a No-Fly and Anti-Terrorism list. Failure is catastrophic in these practical sciences, even at a small scale. You do not begin to practice any of these fields until you are well beyond the beginner stage, and in some cases, you are not even allowed to take part in it until you have mastered it.

Computer Science is different, because the ceiling of failure is extremely low. You want to simulate effects of queueing strategies in a supermarket? Be my guest, any language will work. You want to learn to scrape websites so you can scalp trading cards? Go wild. You can even build a groundbreaking Operating System in the comfort of your own home, with the biggest downside being a bricked machine or two.

Computer Science can feel more akin to an art such as drawing or sculpting, because as long as you have access to the medium you can keep practicing indefinitely. You can use any kind of paper to practice sketching, and inexpensive mediums to learn sculpting. Artists would be pissed off to know that all we need is access to laptop, and then the medium is infinite. Unless you are trying to learn distributed computing, Big Data or want to train an LLM from scratch, your ThinkPad full of stickers that smells like Cheetos will do the job.

An Agricultural Engineer needs a bloody farm. An artist needs to buy canvases or even marble. We write programs that are on the kilobytes to megabytes in a world where disk space is measured in terabytes.

No fancy labs, no government watchlists, no need for willing patients. We can rule out an issue with the medium. Now let’s focus on the culture that surrounds it.

Context is Key

Imagine this: You are an aspiring artist. You dream about drawing realistic and accurate portraits, like the Masters. Do you start drawing full-sized portraits? No, that’s silly. First, you need to learn the basics of drawing: straight lines, near-perfect circles and perspective.

Once you understand the basics and graduate from simple shapes you can start working on individual facial features. You sketch eyes for many days, you then move on to sketching the intricacies of noses. Lips, ears, the different textures of hair. You learn incrementally, cementing foundations that you can then build on top of. The key is continuous practice and incremental learning.

What is a face? The sum of its parts. A portrait with beautiful eyes but an incomplete mouth is probably not going to sell for much (unless you know the right people). The subtle details of a cheek must match the emotion of the eyes, for example. All individual parts must move in unison.

Suppose an art teacher shows up and says: “I want you to draw a complete face, as quickly as possible. Don’t worry about elegance, I just want you to draw a face. Also, you can’t use your pinky finger nor thumb. You have 45 minutes, go!”. Is this a fair assessment and a valuable lesson? No, it’s a stupid way to learn. That’s exactly what LeetCode and other coding exercises are.

Companies want developers that can deliver their products. They want high quality software. They want projects that are maintainable and do not crash when you give them the wrong input. They don’t care if you used a red-black tree without pointers, they only care about meeting stakeholder requirements. Most of the programming problems a developer will encounter in their lifetime are Enterprise Programming issues as opposed to mathematical constraints.

Experienced developers rarely deal with the issues stated in these puzzles. Puzzles, you read it right. That is what they are, puzzles. Puzzles are used to pass time and have fun, they are but a small part of learning. Yet these puzzles are glorified by recruiters, software companies and gurus that want to teach you their tricks to get into a FAANG (or whatever it is called now) company.

Senior Software Engineers and those above them (Lead Engineers, Principal Engineers, etc) tend to reach those high places thanks to their ability to see the big picture. Understanding how small changes to one piece of the codebase can have consequences. Knowing how a brittle deployment process can cause the company headaches and cost it a lot of money. A Senior Developer does not come to be after solving a few thousand coding puzzles.

Guilds and Mentors

In the days of old, if you wanted to become a “professional” in your field, you would join a Guild. Using the city-state of Florence as an example, it peaked at around 21 guilds. Guilds had rules, statutes and even provided services to its members that we now consider “public services”.

If you wanted to become a painter 1 you would have had to join the guild and then be accepted under the wing of a Master of the trade. To claim that it was all fun and games would be silly, they probably would have been harsh, demanding and would have given you tedious jobs. But it is well documented that promising apprentices would have been guided and taught about the trade.

Apprentices would have helped in the workshop: mixing colours and paints, drawing sketches for new works, helping with composition, cleaning equipment, etc. The success of apprenticeship programs spoke for themselves: You can trace master-apprentice lineages spanning more than 5 generations. Leonardo Da Vinci was an apprentice of Andrea Del Verrochio, who in turn was apprenticed under either Fra Fillipo Lippi or Donatello (the facts are still contested).

The apprenticeship system was fraught with favoritism, exploitation and interference from rich families (like the Medici). But it produced some of the most renowned names in art.

As guilds lost their grip and humanity moved into more individualistic modes of life, mentorships took over. In Universities you would attend lectures of famous scientists, maybe even their live demonstrations. Promising minds would be taken in and guided through the field. Again, lineages of master-apprentice start appearing. In Physics J. J. Thomson (who discovered the electron) mentored Ernest Rutherford (who discovered the proton), who in turn mentored James Chadwick (who discovered the neutron). Mentoring lineages in Mathematics have been studied more intensely, with projects like the Maths Genealogy Project. Data from this project uncovered that “over 65% of modern mathematicians are descended from just 24 scholars”.2

Internship programs tend to last a few months in Software Engineering, and permanent mentors are rarely assigned. Even in more established and successful mentorship programs, mentors tend to support their mentees for less than a year. Interns are also usually given tasks that no other team member wants to do. We have all heard horror stories of interns having to label data or execute repetitive UI testing. Most internships have moved away from providing value to both the company and the intern, and are now skewed towards exploitative cheap labor.

Project-based Learning

You learn by doing. You, ideally, want to end up working in “the industry”. And “the industry” wants people that can do it all. They want you to transform abstract product ideas into code, they want your codebase to be stable, they want you to write tests, know what a database is, deploy your project, version control your codebase and more. And you can only learn to do it all, by doing it all.

Universities and coding courses have ditched big projects in favour of small tasks that focus on the subject being taught. The big picture is sacrificed for narrow understanding. This is understandable, Universities do not have all the time in the world, and now that they are for-profit institutions, students don’t have all the money in the world. So your task is to fill that gap with big projects. Write an image viewer that supports JPEG, PNG, WEBP and TIFF images. Write a poker simulation engine that helps you beat your friends. Let your imagination run amok and don’t worry about dreaming too big.

You will likely never finish the project and will end up ditching half-way, and that is okay. My most important lessons in my early programming journey came from my stupid engineering and design decisions that came to bite me in the back. Don’t be afraid to experiment, mess up often. Worst case scenario? A wasted afternoon. Guaranteed scenario? A lesson that you will never forget.

Even a small, unambitious and seemingly simple project is enough to get you started. Let’s break down what building a calculator may look like for you, and what tasks you can set yourself:

  1. The calculator reads 2 numbers and an operation from a string, in a specific order. Humble beginnings. Planning is nearly non-existent, a gentle introduction. You will do some straight-forward string parsing.

  2. The calculator’s codebase is version controlled. You learn about Git, memorize 3 commands and Google the rest. Welcome to the trade. If you did not pick Git, you picked wrong.

  3. The calculator must support both interactive mode and standard input. Your program now has 2 different “entry-points”. You will learn about exit codes, standard input, standard output and standard error. Ideally you go into a little rabbit hole about these topics.

  4. The calculator must work with parenthesis or brackets, and combinations of operations. This really raises the bar now. Your string parsing needs to be much more elegant, and you are now learning about other data structures. You will probably have to engineer your program a little more, and you will probably do it wrong.

  5. The calculator must have passing unit tests. You discover that you can ditch your manual testing in favour of automated tests. Like a caveman discovering fire, you are impressed. And like a caveman discovering fire, you discover how many dangers lie in the dark. You realize that your program structure is hard to test, and you hopefully re-engineer it to make it more testable (and probably do it wrong again).

  6. The calculator should now work with decimals, up to a certain accuracy. Floating points, decimals, rounding and accuracy. You have entered a new level of hell, and you are now learning valuable skills that will be forever useful. You go into rabbit holes about how computers represent numbers. You are now considering ditching programming for goat farming.

  7. The calculator should provide binaries and/or target multiple systems. This fully opens the floodgates to deployments. Your programming language of choice influences what you learn and how you “package”. If you picked Python, for example, you are reading about “wheels” and realize that your code is accessible and modifiable by the user. If you picked C, for example, you discover the usefulness of package managers and curse every step of the way. You also hear about ARM, probably for the first time.

  8. Merges into main/master branch should have policies. You have survived the trenches, and now need to learn about imposing order in repositories. Make backups of your repository, edit its history, mess up branches, change policies and experiment as much as you can. You realize how easy it is to mess up a codebase.

A simple calculator is enough to learn. Any project does the job, as long as it solves a tangible problem (as opposed to coding puzzles). An everyday task you want to automate, or cool simulations you would like to learn about are a good starting point. We make choosing much harder than it really is. If you are no longer a beginner, it still applies. If you are a senior developer at home ready for the next step, go deploy a Kubernetes cluster at home or something. Whatever you do, make sure it’s an entire damn project.

Footnotes

  1. I keep referring to artists because I am an avid reader of Renaissance history and lover of the artistic movements it inspired. If it pisses you off remember: at least it’s not AI slop. 

  2. Modern Mathematics Descends From 24 ‘Mentors’, link