How to be a Great Programmer: Awareness, Understanding, and Responsibility

There are three key factors to being or becoming a great programmer: awareness, understanding, and responsibility.

I’ve talked a lot about the subject of understanding. Heck, I even named my most recent book Understanding Software. In particular, I’ve pointed out many times that the better you understand something, the better you will do it.

However, there are two other factors that go along with understanding to make somebody into an excellent software developer. In brief, if you aren’t aware of a problem, there’s no understanding to be had. And if you don’t take action to solve the problem (which starts with taking responsibility for it) then you can’t do anything about it.

There’s a lot to know about each of these points, though, and they are key points when one looks at how to become a better software developer. So I want to go into each one in more depth with you, here.

Awareness

In a way, this is a specialized form of understanding. “Awareness” means, essentially, that you can perceive something and know that it exists. There are a lot of ways that this applies to programming.

Let’s take one of the simplest: if you aren’t aware of a bug, you can’t possibly fix it.

Sometimes, people use this to get out of having to do something about a problem. If they aren’t aware of it, they think, then they don’t have to do anything about it. That may or may not be true, but what is true is that the problem exists whether or not you personally know about it. If your system has a bug that is affecting millions of users, then you should be aware of it. Otherwise your users are going to suffer. Their suffering is not justified by you being unaware of their suffering—that is, it doesn’t suddenly become okay in the universe just because you don’t know about it.

There are more subtle forms of awareness. For example, some people are not actually aware of code complexity. They don’t realize that the code that they just wrote is complex. Sometimes they aren’t aware that complexity is even an issue, or aware of what the viewpoint would be of another programmer who has to read the code they just wrote. They don’t see that the code is missing comments, that it’s hard to read, or that there is another way to solve the problem. All of these are aspects of awareness for a programmer.

The simplest way that you can improve your awareness as a programmer is to be willing to go out and read code that’s outside your normal realm of control. That is, maybe on a day to day basis you work on code that encodes video. But that video also comes from somewhere—users upload it into your system. If you encounter a problem with how the videos are coming into your encoder, then maybe you should choose to go look at how that code actually works.

This sounds simple but it can be quite emotionally difficult for many people, sometimes even including me. It’s easy to think, “they’re all idiots over there and this sucks and I just have to work around it.” But how do you even know that if you haven’t looked at their code? Maybe there’s something you would learn that would help you.

Awareness can also be about knowing the existence of libraries, frameworks, or tools that you can use in your code. Maybe you don’t know everything about the latest web development libraries, but if you’re a web developer, you should at least be aware that they exist. Then, when you need to make a decision about which framework to use, you know which ones to go look at.

In general, one can improve awareness simply by being willing to experience new programming languages, new design patterns, ideas about testing, new frameworks, etc. I personally want to know about new systems and methods of programming. When I hear about a new language that’s all the rage, I go and at least read the front page of its web site, and sometimes even delve into its documentation somewhat. That way I at least know the thing exists, and I could learn more about it if I needed to. If I’m not aware of different ways of solving problems, then I have far fewer tools at my disposal as a software engineer. Increasing my awareness in this way increases my options when I’m faced with a difficult situation.

This all may sound overly simple, but it is quite true:

You can improve your ability as a programmer by improving your awareness of programming and programs.

Understanding

As I have mentioned elsewhere:

The better you understand something, the better you can do it.

Awareness is a first step—you know a thing exists. After that, you want to understand it in order to do well. Let me give you an example in a made-up psuedocode language:

function print_hello() {
   clear_screen()
   display_hello()
}

What do you think this program does? It looks like it clears the terminal out and then prints the word “Hello.” But honestly, I’m just guessing. I haven’t looked at the documentation or code of clear_screen or write_hello. If I looked at the code, I would see that clear_screen actually turns the whole screen white, and write_hello prints the word “HELLO” in fifteen different languages and colors across the screen. But I never could have known that without actually looking at something in order to understand it.

That may seem like an oversimplified example, but this happens all the time in programming. Now that I know what clear_screen does, I can use it elsewhere without having to think about it or spend time re-writing it. I can write a program that does what I intend it to do, instead of a buggy program. I could write a test for it that makes sense. All of this adds up to simplicity, stability, and all the other qualities that we think of as being “good programming.” And all of that stemmed from understanding.

There are other forms of understanding, which I’ve gone over quite a bit in several articles, most particularly “Why Programmers Suck.” It really is the key point which makes or breaks a programmer. There are other skills that are useful—typing quickly, communicating well, managing your priorities appropriately, and many others. But the key point is understanding. Without that, you could be the fastest programmer in the world, but only write terrible, unmaintainable systems. You could be the most charming person on the team, but fail utterly to write a working line of code. I’m not saying it’s bad to be fast or charming—those are also useful skills—but that without understanding, they are not going to make you into a great developer.

Responsibility

I talked about this a bit in an article on the O’Reilly blog that I wrote shortly after the first book came out, but I’m not sure how many codesimplicity.com readers have read that post, even though it’s actually a very key point about being a great programmer.

Now, first off, I want to clarify something. By “responsibility” here, I do not mean, “taking the blame for something that went wrong.” That’s a common definition of responsibility, but not the one I’m using. What I mean in this case by “responsibility” is, “the willingness to be at cause over, or in control of, some thing.” For example, if I have responsibility for my room, I’m willing to clean it up. It doesn’t mean I have to clean it up, just that I’m completely willing to. And not just a big PR statement about “oh yes, I’m completely willing to clean my room” and then go off and lie down in the mess for three hours. No, I mean really, actually willing. Like, are you willing to eat your favorite dessert? That’s willing. I’m not saying you have to be as excited about everything as you might be about your favorite dessert, I’m just saying that there has to be actual willingness involved.

So how does this apply to programming? Well, all too often I run into this negative example:

“I don’t want to refactor that code because I don’t own it.”

That’s silly. Are you capable of editing it? Can you read the source code? Are you allowed to send changes to the owners? Then you’re theoretically capable of fixing it. Now, that doesn’t mean that you should go around fixing all the code in the whole world all the time, because sometimes that’s not the right time trade-off to be making in terms of your development goals. But even that sentence I’m loathe to have said, because all too often people use that as an excuse to not clean things up, also:

“I can’t spend any time cleaning up that team’s code because it will make my project take longer.” Or, “Well, cleaning up this code that I depend on isn’t part of my team’s goals.”

Okay, there are two flaws with those theories. (And believe me, they really are theories—not facts.)

The first is that usually, it takes longer to do something correctly when you’re depending on bad, crazy, or complex code. Often, it actually ends up taking less time to refactor and then write your feature than it does to try to write your feature on top of some messy or horrible library that doesn’t work at all the way you need it to. So you’re not usually adding a lot of extra time to your goals. The reason that it might seem like you are is that you’re not thinking about the difference between getting something “done” and getting something actually done.

What do I mean by “actually done?” Well, I mean that it works, it’s not full of bugs all the time, you can maintain it easily, it isn’t sucking your whole life up as a developer, and you can move on to doing other productive things.

Think of the problem this way. Let’s imagine that you’re building a wall, and you start off at the bottom with some shoddy bricks. Then you put a few more layers of bricks on top of those. But after you get to about the fourth layer of bricks, the shoddy bricks at the bottom start breaking. So then you go and you patch up the shoddy bricks by adding some shoddy wood over them. You add a few more layers of bricks, and now the wall starts to fall over. So you prop it up with some rusty iron bars and go on with your work. If you continue this way, you’re going to be spending your whole life simply maintaining the wall. It’s going to become an immense problem in your life. You’re probably going to eventually walk away from it and leave somebody else with this terrible disaster of a wall that they now have to maintain, but that they don’t even understand because it looks like a crazy amalgamation of shoddy building materials that nobody in their right mind would ever combine. That’s pretty cruel, honestly.

When you “hack” or “patch” around bad code in your underlying dependencies, you’re building your software in the same way that you would be building that wall. It’s less obvious, because programs don’t have a huge physical structure that can fall on your face. But nonetheless, the same principle of building applies—when you solve an underlying complexity by adding a complexity on top of it, you increase the complexity of the system. When you instead resolve the underlying complexity, you decrease the complexity of the system.

And I want to point out that it doesn’t matter who made the thing complex in the first place. The fact that “somebody else did it” doesn’t change any of the above rules. And it doesn’t matter who owns the complexity now. If you hack around it, you will make the system more complex. If you fix it, you will make the system less complex. You are making a choice—you have this power, you can be responsible. Yes, sometimes that involves getting somebody else to fix it. But in my experience, even more often it involves you being willing to make the change yourself.

And sure, maybe you know all that. But still, you might think, “Oh, well, I can just make this one tiny piece a little bit more complex this one time, because it’s just this one thing that I’m doing.” And you know, sometimes, you might be right, especially if you have some sort of legitimate emergency where you have to hack something out for just a short time. But more often, you’re actually contributing to a huge mess of a broken wall that will become a nightmare for you and everybody else to maintain. It’s these little complexities, these little choices to not be responsible, that eventually add up to the giant broken messes that nobody wants to deal with.

So when I say “responsibility” in this way, a big part of what I mean is, “be willing to change things outside your normal perimeter.” It doesn’t have to be an infinite perimeter. You can draw a line somewhere and say, “Beyond this point, it really is somebody else’s problem.” For example, on my projects I have often drawn a line that says, “Okay, I’m not going to do as much work on code that comes from outside the company entirely, because that’s not as productive of a use of my time.” But I have from time to time even gone as far as filing bugs against programming languages or sending patches to developer tools when I thought they were causing complexity or making my life more difficult. I mean heck, I actually became the primary architect of Bugzilla because I thought there were a lot of things about it that needed to be fixed. I’m not saying that everybody should go that far. But I am saying that you will become a much better programmer by accepting responsibility for some code beyond the scope of your project. And that the wider you spread this scope out, the better of a programmer you will become.

Oh by the way, I said there were two flaws with the theories above. The second one is that you actually belong to a group, even if that group is just humanity. You’re not the only person around. It’s okay to contribute to other people’s code. We’re actually all kind of on the same team.

If you’re working for a company, you’re helping the company as a whole by fixing these things when you come across them. And if you’re just an individual developer out in the world, you’re making the world a little bit of a better place for every other programmer when you fix some widely-used library, some popular tool, or some bad sample code on the web somewhere. And honestly, it makes you a better programmer. That’s the whole point of what I’m saying here. The best programmers that I’ve known are the ones who are the most willing to take broad responsibility for everything going right no matter what piece of code they have to touch or what team they have to talk to to get things done. I’m telling you this because it will help you.

And by the way, as a consumer of a library or API, often you are in the best place to refactor it, because you know better than the authors how that library or API should be consumed. At the very least, file a bug against the code saying what problem you had with it. Otherwise how will the authors ever know that there is some problem? You might just expect them to magically know, but believe me, very often they do not know. Your experience could be very valuable to them, who knows!

Summary

Overall, if you want to be a better programmer, the thing to ask yourself is whether you should improve awareness, understanding, or responsibility, and then focus on that for a little while. If you aren’t sure, start with awareness, then go on to understanding, and finally take more responsibility. It’s very difficult to do it in the other direction—you can’t easily take responsibility for something you don’t understand (it would just be very confusing and you’d not be very good at it), and it’s impossible to understand something you’re not aware of. So awareness, understanding, and then responsibility is the right sequence.

If it’s awareness that you should improve, just read some new code, find a new programming blog, look around for book titles, talk to some other programmers about the latest technology—anything that just helps you become more aware of problems, solutions, knowledge, patterns, people, organizations, principles, or anything else that would help you in your job.

If it’s understanding you’d like to focus on, then read some more documentation, spend more time understanding how each function works, ask more questions of your fellow programmers, look up some words in the dictionary, read some articles about the technology you’re using, or read a book—any method of coming to a complete and full understanding of some knowledge related to your job.

And finally, responsibility is achieved mostly by just deciding to take on more. When a problem comes your way, decide to solve it rather than put it off. When there’s a difficulty outside your team, decide to help resolve it rather than be part of the problem. And there is a special type of responsibility, too—when somebody else has a problem to solve, be willing to help them, or, be willing to let them solve it on their own if that’s what they should do. Responsibility doesn’t just mean that you have to do everything. It can also mean being willing to help other people get things done, too.

It’s not even hard to do the above—as long as you do it in simple, individual steps. You don’t have to become aware of the whole universe overnight. You don’t have to understand every word of every program ever written, tomorrow. And you don’t have to be willing to change every piece of software in existence just because you read this blog post. Start with something small. Then move on to the next thing, and the next thing, and the next thing, and in time, you’ll be just as good of a programmer as you want to be.

Enjoy.

-Max

9 Comments

  1. I agree with you, and in fact, I believe that programming is a sort of craft. The tool are given and normally (even studying in an engineering school) you develop you style (craft) with you guts; even if your guts tells you to forget the main principles of software engineering.

    But as you said, you decide to be responsible of your stuff; and very often we forget that the code is kind of a live specimen that evolves and grows, and complexity over complexity could kill us (or just make the code unusable).

    At this point I think that a craftsman has to evolve into an engineer, not only knowing how to write a piece of code that works, but developing tools or mental processes that help himself and others to make easy to develop software.

    Which is also so difficult, you have to define your limits by avoiding to reinvent the wheel but being willing to learn and being brave enough to decide to reinvent the wheel.

    Greetings! And happy new year!

  2. For me, all about programming is to experiment on codes. And while doing that, a self-aware programmer-to-be ought to focus on each and every element of the process. I really like this awareness-understanding-responsibility division of yours – it exactly depicts how the stuff works during learning.

    Cheers and thanks for an insightful content!

  3. This is very good post. As a developer this post help me very much. This is right that a good programmer has three type of knowledge. If any one of this three item is missed,he/she is not a good programmer. According to my opinion there are so many tool available in the market for this reason a software engineer is forgetting his main aim. thank you.

  4. I think you nailed it with the three main categories of a great programmer: awareness, understanding, and responsibility. Though I would challenge you to also add perseverance and patience. A lot of our time as Software Engineers is spent simply trying to understand the problem as you outlined in the introduction. This step in my opinion exemplifies the need for a great software engineer to have patience — we all know it can be difficult to work with those less tech savvy to determine requirements. Finally I wrap up with perseverance, much of our job as software engineers is to overcome adversity. To fix the bug in production or to push it live untested, these are real situations that happen to many of us daily. Great programmers must persevere! That’s all I’ve got but thanks for the read.

    Cheers!

  5. In my opinion not everyone can become a great programmer. A good programmer yes. But to be great at programming takes something that cannot be learned. Great post

    • Hey Carolyn. No, I disagree. I think anybody who has a genuine interest in becoming a great programmer could theoretically be a great programmer. There can certainly be barriers and there is some factor of aptitude, but it’s possible to adjust even that.

      Glad you liked the post! 🙂

      -Max

  6. Greetings.

    As an absolute novice in the world of programming, I found it helpful to read this blog, you really inspire me to improve and learn more about a subject that I love and sometimes frustrates me. I think it’s the learning curve at the end of everything. A hug and thanks for sharing some of your wisdom, greetings from Colombia.

  7. I am also being a software developer for a while ago, it was really hard to absorb any code without any suitable logic’s. well i can say a logic word because all the code has some manner of execution level. for e.g. you have to design any suitable software system like Hospital management system, first of all you have to make a document of their primary UI panel.

    As i said panel means the front dashboard which s easy to understand. i have seen many system software which have main knowing panel of blue dashboard but their back-end work is very complicated.

    Back-end & Front end Development – first i am talking about the back-end development which is very very complicated and hard to absorb, all the main functional work from back-end development.

    Nowadays Front End Development is no more the easiest platform for the development, you have knowledge of some sort of programming basics to intermediator workflow.

Leave a Reply