“What are the levels of skill of a coder?”
The answers that I get, usually, were something along the lines of: “fresh grad, mid-level developer, and senior developer”, or “software developer, software engineer, and software architect”. My guess is that this is based upon what the companies they work for have structured their development teams around.
This is an easy means of classification, of course - to simply use the classification already thrust upon you by your management.
Here I’ll posit an alternative means of thinking about coding skill, that is not based so much around levels of skill in writing code, rather based around traits and behaviours when writing code. It is going to be highly subjective, but then again, you probably already got that from the title!
To think about coders, you must of course think about their main craft: the code or the software that they write!
… so - what is software, or what is code, really?
At a fundamental level all software is data, and decisions about that data.
That is the day-to-day challenge faced by all coders. For any coder, no matter what software they write, no matter what the project is they are working on, no matter what company they work for - it always boils down to data, and decisions made about that data. This fundamental nature, of course, is a double edged sword: it is a simple philosophy, but simultaneously extremely open-ended.
The number of possibilities are endless - let’s consider the following:
How do you structure your data, where do you store it, and in what format? What does the data actually mean? What operations do you perform on the data, in what order, and how do you optimise them? How does this data related to this other data, or influence it? The sheer number of ways you can answer each question - for any given task - creates a great many deal of decisions that need to be made. In many cases it can seem infinite.
… and it’s mind boggling.
The coders who have to make these decisions, of course, do not have infinite time - or patience - to deal with all of them. Coders are human, after all!
… so coping mechanisms are employed.
Coping mechanisms are means with which people deal with things that are beyond their capability/ control. It’s a way to reign in those things, and still be an effective person. In that sense, they are great; but of course, the drawback is that they are inexact, and at times are short cuts, instead of taking the proper route.
This is a big part of why, IMHO, it is not possible to classify the act of writing code into either an art or a science. Its logical and mathematical nature, plus its roots in electrical engineering make it a scientific endeavour. However, the expressiveness of the languages, with its own flavour of spelling and grammar rules, and its open-endedness, with a myriad of ways to express the same thing, certainly make it an artistic endeavour as well.
… but enough pre-amble!
At the early stages, the most common trait in writing code is simply to copy what someone else has written. I call this the copy-pasta.
Doing something for the first time, and stuck figuring out how to do it? Why not just Google the relevant key words, and chances are, you’ll find that someone has already asked a very similar question on Stackoverflow, and since the question was so similar, the answer most likely is as well!
A couple of well-worn key-stroke combinations later, you have copied the relevant snippet into your your own code, and run it to test if that has indeed solved the problem.
Here’s the thing: after a while you get very good at this skill of knowing exactly what to search for, and copying and pasting the relevant parts into your code. Sprinkle in a few modifications here and there to adapt the snippet to the rest of your code, a few trial-and-error runs, and you usually get the job done, and solve the problem. You can get very far as a coder, simply by mastering this skill.
“I don’t believe that copy-pasta coders should be used anywhere, maybe if they are copy-eat-cook-pasta coders: You may read, but you may not paste until you understand what you’ve read! :-)”
- Taco Kemna
Just doing this alone isn’t enough though. You might be able to maintain an existing code base, fixing bugs and adding minor features here and there. That is totally fine, because that is precisely what most line-of-business software needs.
By default, software is in an almost perpetually broken state. If you are the end user of any software - this includes apps, websites, et cetera - and think you have it bad when software is buggy, spare a thought for the coders who wrote it: What you experience is way, way, better than what the coders deal with when coding it!
After furiously coding for several hours at a stretch, there is a brief moment where the software actually works as it should, and in that brief moment, we capture that precise state, tag it, build it, and release it, and ship it or deploy it to customers. This doesn’t last for very long - the code is broken again, because you started coding the next thing.
The code sitting in your text editor, the code that you, as a coder stare at on the screen, is in this perpetual cycle of peaks and troughs. 1% peaks, and 99% troughs. The truth is that as a coder, you spend less time actually writing code, as you do stepping through code in a debugger, inspecting the data, and inspecting the output logs. All of this to figure out where something went wrong.
We have all been there!
So actually getting things to work at all was an amazing feeling. When something has been broken for several hours, or sometimes even several days, and you have been plugging away at it furiously, trying all manner of things to get the code to do what you want it to do, and you finally crack the problem, and type that final line of code that fixes the problem, hold your breath while you verify that it works, and unlike all the previous times… lo and behold, it works!
That feeling is amazing.
I liken it to the feeling that you get after putting down the heavy objects that you have been lifting at the gym. You feel lighter - almost weightless even - because it is suddenly less effort to stand up than immediately before; and at the same time you get this post-exercise high with this all-round feel good feeling in your muscles and in your head. Both of those wear off after a couple of minutes of course, and then you go in for your next set of lifting heavy objects.
Copy-pasting code is what gets you to that feeling of gratification quicker and more frequently. After a while though, that elation that you get, when you actually get things to work is no big deal any more. After all, software is supposed to just work right? What’s the big deal that you got it to work for just that tiny sliver of time, for just that fleeting moment?
At that point, you have the realisation that: You need to do something more than this. You need to get more consistently good results for the effort that you put in. You think about your craft more “meta” way - what is the over-arching theme to the software, how can I make this code better, and how can I make myself a better coder?
This realisation culminates in a shift in approach. A shift away from ad-hoc solutions to ad-hoc problems; and a shift toward meta solutions/ generalised solutions to repeatable problems. Instead of copying or following individual snippets of code, you start copying or following patterns.
Patterns in code manifest themselves in a number of different ways, most commonly in the forms of: software design patterns, software libraries, software frameworks.
Remember that code is data and decisions about that data.
Well by following patterns - be they by incorporating design patterns, or utilising a library or framework - you as the coder have made a conscientious choice to have many of those decisions made for you. Let’s take a look at a couple of examples:
Design pattern: Singleton.
By using a Singleton, you do not need to pass around an object instance everywhere that needs it; you simply need to refer to it by type.
Library/ framework: UnderscoreJs.
By using UnderscoreJs, you do not need to implement your own means with which to do various common functional programming tasks; you simply need to invoke the functions defined in this library.
These things just got elevated to best idea since sliced bread, because they solve your code problems not just on an ad-hoc basis, but in a repeatable way, and often solve them in ways that pre-empt problems, and thus result in many less problems encountered to begin with.
Someone else has already solved these various common problems in a general manner, and for the most part in a robust manner to boot! All you have to do is to re-use the solutions to those common problems by adapting them to the specifics of your software, leaving you to focus on the rest. You are guided on how to structure your data, and guided on the decisions that you have to make on the data. This reduces your workload, leaving you to focus on a narrower part of your software, making you more effective as a developer.
There are so many design patterns out there, and there are so many libraries and frameworks too. A new one seems to crop up so often that you barely have enough time to learn and use one of them properly before its replacement comes along. It is super easy to get sucked into this cycle of permanently trying out the next new thing, of course, because of a couple of fundamental psychological reasons:
However, throwing more patterns and libraries at your code base is not necessarily always a good idea. The more design patterns used in your code, the more difficult it becomes to interpret your code. The more libraries used in your code, the larger the API surface area that you have to be across in order to understand how to work with your code. The over-arching theme here is that your code has gotten too “meta”, everything has been over-generalised or over-abstracted in order to fit into particular patterns’ or libraries’ requirements. Sometimes a combination of them are outright contradictory, but most of the time they simply increase the mental overhead, by being hard to grok when put together, and feel “forced” when used together.
Cue for the next realisation to hit home.
அளவுக்கு மிஞ்சினால் அமிழ்தமும் நஞ்சு (In excess, even nectar is poison)
While using more good patterns and libraries in your code means that you get all the good stuff that comes with them; when used in excess they start to harm the project, as you will soon come to realise.
You soon start asking yourself the following questions, whenever considering whether to use a new library, or add a new design pattern:
You’ll subsequently become more restrained when writing code: By scaling back on doing new things only because they work - now they must work, but also yield some additional benefit. By scaling back on doing things because they are new and shiny and the compulsion to try them out. By understanding that when code written in the past has an influence on the way that code is written now, and therefore have a ripple effect on the way future code is written as well.
Code itself may be transient, but its effect is semi-permanent. Many a time you have come across a project that you need to work on, but get that feeling of dread - a feeling that you’d prefer to avoid touching this code if possible. Well, that feeling is usually because your restraint antennae have kicked in, and they’ve sensed that there’s a history to this code. Its all gnarly, ugly, and unpleasant to work with, and sort of grown into what it is right now because of excessive use of libraries and patterns in the past.
You become more restrained, by becoming more selective. More picky, and less likely to OK the use of a new pattern or library. You would have been burned already, several times over in the past, with the after effects of letting too many of these things creep into the code. You’d have had to deal with the after-effects of code whose complexity has gone out of whack, code that even a competent person looking at for the first time would not be able to pick up and dive into right away, code that would be hard to modify in any way in the absence of its original author. Scratch that - code that befuddles its original author too, after some time has passed!
The restraint derives from the irony that all of these patterns and libraries were added with the initial intent to make the data simpler and to reduce the complexity of the decisions about that data. However, through overuse, the data actually started getting more complex, and the decisions to be made about the data actually have become harder to understand.
These traits and behaviours of coders heavily influence the type of software that they write. You’ve got the copy-pasta coder who solves problems ad-hoc by copy-pasting snippets of code from the Internet; then you’ve got the easily-excitable coder who attempts to use as many design patterns + libraries + frameworks as possible; and finally you have got the restrained coder who is picky and says no to adding most new things.
There’s a natural progression as well: A total newbie quite often starts off as a copy-pasta, and then becomes an easily-excitable one, and finally becomes restrained one; in chronological order.
Most coders have a dominant trait amongst these three ones, the dominant one usually being the one acquired most recently.
Professional software development is rarely ever done as a solo pursuit - usually we are organised into teams (or scrums, or whatever label teams are given) A good team needs people with all of the above traits, they need to keep each other at bay in order to balance them.
This balance is sufficient to create a team that works well in writing good code. Better yet, we could all strive to be balanced within ourselves, and wear the different hats at different times as appropriate. After all, if each coder within a team was themselves already balanced, then the team would be balanced by default - and no-one needs to feel awkward for telling what the other person should change about the way that they code.
If the team is comprised of coders with very very strong bents toward any of these traits, the teamwork is going to be akin to rusty door hinges: the door still can opens and shut, but it is going to make a lot of noise and irritate anyone nearby. If each coder in that team can balance the traits within themselves, that’s like a spraying some WD-40 on the hinge.
The act of writing code is both an art and a science, and at a fundamental level it boils down to data and making decisions about that data. This can be infinitely complex, because of the sheer number of ways to do each thing, and the permutations and combinations thereof.
Coders are human beings, and human beings cannot handle infinite complexity. Thus they seek ways to be still effective and productive at writing code by managing this infinite complexity. This manifests itself in the form of three common traits & behaviours: (1) the copy-pasta, (2) the easily-excitable, and (3) the restrained; and most coders pick up these traits in that order.
Code is often written in teams, and those teams are most effective when they are comprised of coders who exhibit a balanced combination of these traits - wearing different hats for each one, at the appropriate time. Individual coders are most effective when they balance these traits within themselves; and teams comprised of such coders are most effective.
Copyright © 2008-present Brendan Graetz