It's dangerous to code alone! Take this.

Can self-taught programmers make it?

Published on 19 Nov 2023.

This question comes up on Discord often, and I wanted to turn my answer into a blog post:

Can self-taught programmers make it? How hard is the self-taught path vs. going to a university?

The short answer is, of course it is possible to “make it” without formal education.

The longer answer is more complicated.

The benefits of a formal education

Let’s look at career paths for a moment.

If you can land your first full-time software engineer position and excel in it, then you won’t generally have a hard time moving around or getting promoted when the time is right. It’s that first job that’s the hardest. Indeed, after you have about five years of professional experience, people mostly stop caring about how you learned it. So the trick is figuring out how to get that first job.

The bad news: at all the jobs I’ve worked at, most of the people who get hired do, in fact, have a four-year degree in computer science or software engineering. Getting the degree is expensive and time consuming, but it somehow changes people’s perspectives on things.

But it is certainly not a deal-breaker. At least not for most companies.

From my perspective, as somebody who is deeply involved in the hiring of software engineers where I currently work, I see two major benefits to a four-year degree:

  1. It shows you can work on a hard task that takes years to complete, one step at a time. By comparison, people who are self-taught often don’t have such a massive undertaking on their resume. Most software engineering jobs are literally about working on a project for years, and taking it to completion. To be sure, the specific degree doesn’t matter for this. Any degree creates this sort of “proof of willingness and capability to do hard things.”
  2. It “guarantees” foundational knowledge, skills, and experience needed to begin a career. When you get a formal education, you’re required to take classes in programming fundamentals, software design, data structures and algorithms, operating systems, concurrency, assembly programming, how the hardware works, and then a smattering of choose-your-own-adventure-style courses covering things like AI, testing, databases, game programming, big data, UI programming, mobile, graphics, robotics, computer vision, web, etc. Importantly, because the curriculum is organized and taught by people who nominally know what they’re doing, your knowledge, skills, and experience isn’t Swiss cheese–it doesn’t have a lot of holes in it. And what holes exist, you’re generally aware of.

There are plenty of caveats with both of those. I’m talking in broad strokes here.

Getting the right experience without formal education

But if you want to bypass the formal education route, then you need a counterbalance to offset those missing pieces.

In particular, you’ll still need to show that you can work on hard, scary challenges that span years. And you’ll need to ensure that you have a solid foundation of knowledge, skills, and experience without a lot of Swiss cheese holes in it.

For somebody who can’t afford the cost of formal education, or for people that don’t fit nicely into the “box” that formal education tries to squeeze you into, here are my suggestions.

1. Ensure the code you write looks professional.

This is the easiest item on the list. One of the biggest differences I see in self-taught programmers vs. those with formal education is the self-consistency of their code. A lot of self-taught programmers will write code like this:

public class Score
{
    private int points;
    int Level;
     string _name;

    public Score(int p, string name, int lvl) {
        points = p;
        Level = lvl;
        this._name = name;
    }
    public string GetDisplayedText()
    {
        return _name + " scored " + $"{points} points and reached level {Level}.";
    }


}

Contrast that with this:

public class Score
{
    private int _points;
    private int _level;
    private string _name;

    public Score(int points, int level, string name)
    {
        _points = points;
        _level = level;
        _name = name;
    }

    public string GetDisplayedText()
    {
        return $"{_name} scored {_points} points and reached level {_level}.";
    }
}

It’s functionally identical code, but it is self-consistent.

  • private is consistently applied to the fields.
  • The fields are consistently _underscorePrefixedLowerCamelCase.
  • The curly braces are consistently Allman-style braces, each on their own line.
  • The whitespace between methods is consistently one line between major members, with no random extra lines.
  • The order of the parameters in the constructor match their order they’re defined in the class.
  • The GetDisplayedText method is consistently using interpolated strings instead of some interpolation and some +-based concatenation.

Consistency goes a very long way to helping readability, and it seems like this is something long-time programmers eventually have beaten into their heads or they just naturally learn. You can “fake it” by just being meticulous about consistency.

As a corollary, self-consistency is the most important thing here, but consistency with the rest of the world is a close second. Allman-style braces is conventional in C#. Some people learned Java before C#, and got used to K&R-style braces, with the open curly brace on the end of the line before it. If you’re self-consistent, that will still generally look professional. But, as they say, “When in Rome, do as the Romans do.” Your personal code style for a language should match the common conventions of the language for 95% of the decisions you make.

Of course, “professional-looking” code is more than consistency, but it is the single biggest thing I see people get wrong. It is a small thing that goes a very long way, so start there.

But for other guidance on writing professional-looking code, be sure to carefully analyze code written by long-term, professional programmers. (Not other students. Look to the experts.) Look for the patterns and idioms they use. When do they use a while vs. a do/while? When do they use switch vs. if? Mimic it.

It can be a useful exercise to write down your own personal style guide. By writing down what rules you’re using for capitalization, whitespace, formatting, how you name variables, when you make comments, etc., you can set your own expectations, which makes it easier to follow through.

2. Invent your own curriculum.

The people with a formal eductation had the luxury of somebody else defining what they ought to cover. You’ll need to take that burden on yourself.

There are two major sources of information that I’d use as inputs:

  1. Look for jobs that you’d be interested in, and see what requirements they have. Specifically, look for trends. If you want to make web apps (a very popular area), then you’ll be on the lookout for trends like most people calling out Angular or React, or ASP.NET or some particular database flavor, or whatever.
  2. Look at the required (and optional) courses for universities that you’d attend if things were different.

Then build your own curriculum from that information. Keep in mind, you won’t just go do all of this stuff all at once. It will take time, and some topics can wait.

This will vary from person to person, depending on what jobs you’re interested in. But here’s a high-level general-purpose view, from my perspective:

  1. You need to be proficient at at least one programming language. Pick your poison, but C# is a great one. JavaScript, Java, C++, C, and Python are other good ones. But you’re not just looking for familiarity. You need to be good enough to solve nearly any problem that will come up using the language. Proficiency, not familiarity. That means using it a lot.
  2. Data structures and algorithms. You must know the common data structures and algorithms. Array-based lists, linked lists, stacks, queues, hashtables/dictionaries, graphs, trees, etc. Binary searches, sorting algorithms, recursion, etc.
  3. Software design. Most widely-used programming languages are object-oriented, so things like abstraction, polymorphism, etc., should be second nature to you. You should have familiarity with design patterns, the SOLID principles, and should have a giant pile of experience behind you in figuring out how to structure code that is more than, say, 500 lines. But it isn’t all object-oriented. There are plenty of other paradigms out there that you should have some familiarity with, including event-driven, functional, data-oriented design, etc.
  4. Version control. Learn Git, at least well enough to do the basics.
  5. Unit testing. You should be able to write tests for the code you write. You don’t need to do it 100% of the time, but you need to know how to test stuff.
  6. Learn how computers actually work. Few people will ever program in assembly, but it’s the computer’s language. x86-64 is about as tricky of an instruction set as they come, so you could consider tinkering with MIPS or some other simpler instruction set. Or, minimally, go buy a copy of Human Resource Machine and play it. Maybe throw in TIS-100 for good measure. But this also requires spending some time studying how the hardware works. How a CPU executes instructions, how RAM is read and written to, how registers work, how the CPU’s cache works, etc.
  7. Learn how at least one operating system works in detail. This probably includes being able to do scripting for that OS. Bash or PowerShell, for example. You don’t need to become an expert, but you do need the foundations.
  8. Concurrency and multi-threaded programming. Most modern computing requires some amount of dealing with threads, deadlocks, mutexes, etc. Again, you don’t need to be an expert, but you do need to have some actual, practical experience with it.
  9. Programming languages. You should learn more than one language, and you should learn about how programming languages are designed and how compilers work (lexing and parsing and abstract syntax trees and all that good stuff). Again, you don’t need to be proficient in multiple languages, but pick a few that are wildly different from your language in #1, and do some tinkering. Learn some Haskell to force your brain to think functionally. Learn some Prolog to see a wildly different syntax (a logical language). Do a little C programming or a little Lua programming.
  10. Pick three of the following “electives”:
    • Network programming. Learn about TCP, HTTP, UDP, etc. in depth.
    • AI. The large language models have been a big deal lately, and deserve attention, but don’t limit yourself to this. Learn about things like reinforcement learning, search problems, etc.
    • Databases. Use more than one database engine, and get good at writing queries in the built-in query language, but also from within a program like C# (maybe using Entity Framework).
    • GUI programming. Learn how to make desktop applications using something like MAUI, WPF, or WinForms.
    • Web programming. Learn how to make web applications, maybe with ASP.NET (Core).
    • Mobile app programming. Learn how to make mobile apps that work on iOS and/or Android. Maybe use MAUI, React Native, or Flutter.
    • Game programming. Learn some Unity, MonoGame, Godot, or Unreal. Build your own engine, even if it is crap. Make some clones of simple games, and design your own.
    • Graphics programming. Learn OpenGL, Vulkan, or DirectX. Learn GLSL or HLSL. Write a ray tracer.
    • Robotics. Interact with physical hardware. Write a driver. Move a robotic arm. Use image processing or lidar to “see.”
    • Something else. This list isn’t meant to be exhaustive. Computers are used for anything and everything these days. Find a technical area that interests you.

It’s a lot, right? It will take time.

3. Find high quality resources to guide your learning.

In my opinion, the hierarchy of quality learning resources is something like this, form worst to best:

  1. None. Wing it. Do whatever pops into your mind. Look up stuff on Stack Overflow or ChatGPT in a disorganized patchwork, and copy/paste that stuff into your code in the hopes it will work.
  2. Follow some random dude’s YouTube channel or blog. Now you’re getting into looking at an “expert’s” version of things, which helps. Keep in mind that many people start making YouTube videos when they know about 30 seconds of more content than you. Look for the good ones.
  3. A well-structured, large, and well-reviewed “deep dive,” such as a book or course that you pay for. For me, books have been the secret to quickly absorbing what it took an expert years or decades to get good at. In a few weeks or few months, you can view what somebody else spent half a career learning the hard way. The right online courses through things like PluralSight or Udemy could work. But all of these can be hit-or-miss. I’ve read some books that weren’t useful. And I’ve seen some Udemy courses that just didn’t get into any meat, taught by somebody who really didn’t know how to teach, or even knew the subject matter well. Look for ones that are loved by people you trust, for the most part, but it’s okay to take a chance here and there, on one that looks good but doesn’t have a lot of reviews yet.

That’s not to say that there is no place for #1 or #2. I just find them more useful after you’ve got the foundation under you. Random blog posts, YouTube videos, and StackOverflow all make a lot more sense when you can plug them into a solid foundation. Relying solely on those things is exactly where that Swiss cheese knowledge comes from.

In short, take your learning seriously, and be willing to find good resources–and even pay a bit for them. A $40 book is still way cheaper than the thousands of dollars that you’d pay for a semester of college.

4. Projects, projects, projects, projects.

To offset some of the things missing from a formal education–and to develop that knowledge, skills, and experience–you must be building projects, and lots of them.

Your resume won’t have a degree (or previous programming work). You need proof that you can program. That comes in the form of programs you have created.

You want things that aren’t trivial–let’s say more than 500 lines of code, as a minimum. But you also want things that are very big too.

Perhaps start in an area with several week-long projects, then graduate to some month-long projects.

By the time you’re “done” the ideal would actually be to have at least one project that you’ve worked on for a year or more.

These projects will boost your resume–but more importantly, they’ll give you the knowledge, skills, and experience needed to actually get a job. Growing that foundation is the most important thing you can do.

5. Try to make at least a few “finished”, sellable programs.

If you can actually get a program or two to a point where it’s “out there” and people can actually download and use it, you’re way better off than most.

If I’m looking at a candidate that is self taught and their resume only claims they can program in C#, but have no degree or work experience to back it up, it’s a no-go for me.

If they have a link to GitHub, and I can poke around at their code, I’ll probably take a peek.

But if they’ve published an app that is an “official companion app for the Xerces 3.1 RPG D20 System” and I can go download it and use it, well… that’s honestly pretty compelling. This is a person that knew how to talk to “the Xerces People” about doing something official. They’ve dealt with a development parner telling them what the app needs. They’ve dealt with paying users that are upset about some bug. They’ve jumped through the hurdles of getting an app on the Google Play store. That’s saying quite a lot!

And… perhaps you’ll end up just making some apps that make you money and won’t ever need to work for “the man” in the first place. Realistically, probably not, but it definitely happens!

6. Try unconventional entries.

The school-centric model for getting your first job is get the degree, and when you’re nearly done, get an internship (if possible), and then apply for “entry level” programming/developer/software engineer positions.

When you don’t pick up the degree, it is far more common to get your foot in the door through other pathways. Consider things like:

  • An IT job where you write a lot of scripts.
  • A testing job where you can get familiar with the development process, and eventually start writing automated end-to-end tests.
  • A temporary job doing some programming work for somebody you know.

I have seen lots of people get to their first programming job by going through one of these routes, and I’m sure I’m missing some.

7. Apply way before you feel ready.

Secret: You’ll never actually feel ready, so don’t wait until then to apply for any particular job. You may not get feedback. That’s okay. No real harm done.

But you may start getting calls and interviews. Interviewing is its own skill that is hard to master without sitting in a bunch of interviews. Your first technical interview probably won’t go so well. It’s hard to name why. Nerves and not being sure what the expectations are.

Applying for jobs gets you this experience… eventually.

And you may be surprised with the result, and get an offer! (Or maybe they’ll say, “We don’t think you’re ready for a full-time position, but we think you show some promise.”)

8. Recognize that it is a journey, and it takes time.

Every day you spend time programming and learning makes you more useful to potential employers. It will take a lot of time–years, most likely.

Make sure you’re enjoying the learning and growth process. It isn’t always fun. It doesn’t need to be. But you are hopefully finding satisfaction in it–even on the days when you spend four hours hunting down some weirdo bug that shouldn’t be able to exist!

We’re All Self-Taught

The last thing I want to touch on is this: we’re all self-taught. The good programmers learned 90% of their stuff on their own. They got intrigued by hard questions or they wanted to try their hand at making something specific, and went out and did it. Not because school told them to.

The formal education merely provides the infrastructure and framework needed to grow in the right directions.

I started programming when I was 10. I made games in Logo, and then games in Turbo Basic, and then finally games in C++, all before even taking a programming class in high school. Yes, the courses in college added a lot of knowledge to my foundation, but all through college, I kept doing a lot of my own stuff. The formal education broadened my knowledge, filled in some holes, and complemented my own projects. But the heavy lifting was done during all of my “spare time” projects.

The vast majority of programmers–certainly of the ones that I’ve seen excel–have this burning passion for making software that drives them to jump into a game jam every few months, to buy Clean Code and make some time to read it, and to spend the occasional Friday evening learning to use and then abuse the C# debugger. If that’s you, then it will show and you’ll do great!