The One Skill You Need To Develop As Programmer

You might have set yourself the resolution to finally learn programming this year. Or your earlier attempts at learning to code never quite took off. There is an infinite number of things to learn once you start looking. But there is one key skill you need to develop — no matter which path your programming career takes.

Note: This post includes some simple code samples in Javascript. Even if you have never read a programming language before, you can probably tell what’s going on just by reading them!

Typing is not coding

There is no shortage of learning material, if you want to learn programming online. Blogs are full of tutorials — from the basics to specialized frameworks. YouTube is overflowing with videos showing you step by step how to achieve a programming task. Educational websites like edX and Udemy teach computer science with a comprehensive approach.

But pretty soon you will discover (like I did too late in my programming career) that watching a video or reading a post (except for this one 😆) doesn’t make you a better programmer. In fact, if you don’t use the instructions you get from content online immediately in your projects, you will forget them soon.

The satisfying feeling to have educated yourself with programming topics instead of watching Netflix fades into a realization that you spend a lot of time watching other people type.

Don’t get me wrong. Online tutorials are a great resource, and you need them along your journey. But some crucial coder skills you don’t learn that way.

Why is there not a clear way to learn to code?

Even though we still disagree about how to learn ancient subjects such as mathematics1, we have had a lot of time to find ways that do and do not work. But programming itself is a young discipline. And research on how to teach it is even younger.

Many researchers have hypothesized that you need to be good at math, language, or both to succeed in programming. The connection to math seems obvious. You have statements and algorithms that are often connected to mathematical concepts.

The language argument stems from the similarity of programming languages to our written word. You can identify syntax and structure. Why would a method that helps someone to learn a new language not work for picking up Python or Javascript?

I believe that asking which other field programming education mostly resembles is the wrong approach. As a developer, you will combine skills from several related fields. You will certainly need mathematical concepts in your career, but that does not mean that you should learn to code like you prove a theorem.

If you want to become a frontend developer you should not only learn how to write code, but also acquire a basic understanding of user interface design. If you want to become a data scientist or machine learning engineer, you need to learn to program. But you will also need a good grasp mathematics and especially statistics to succeed.

Predict the future

However, there is one skill that you will most certainly want to develop when you embark on your programming journey. This skill can best be embodied by Doctor Strange from the Marvel Comics.

Doctor Strange can peer into various futures to see their outcomes.

I know what you are thinking.2 WTF Johannes, I want to learn programming and not acquire parachronal cognition. When you are programming, you are actually doing a very similar thing. You have to anticipate all the ways in which your code might be run.

Another way to understand this skill is to consider chess. Grandmasters consider the consequences of their potential moves often several rounds ahead. This is how they can decide on the best move forward.

There are two straightforward ways you can acquire this ability when you code. In the coming section, I will show you both with an example.

Imagine a simple signup form on the web. To structure the business logic of signing up you need to create a form that accepts an email and a password. But what happens, if the user provides fakeEmail into the email field? How will your program react? It is these considerations that make up a key skill set of a programmer.

Work with pseudocode and comments

One way you can train your parallel universe superpowers is to write the steps of your program in comments. This can be a quick abbreviated form that steps through the correct logic. Think about all possible scenarios of your code.

Code comments help you quickly think through what you are about to write. But be careful not to write your code like one logical sequence of steps. I will give you an example of what I mean.

Let’s look at our email form again. We want to validate what happens if a user enters a fake email address, a password shorter than 8 characters or, correct information.

Our pseudocode might look like this:

// check `email` field
	// if `email` field is not valid email => return error object:
	// [{ field: 'email', message: 'The provided input is not a valid email'}]

// check `password` field
	// if `password` field is shorter than 8 characters, return error object:
	// [{ field: 'password', message: 'The password needs to be longer than 8 characters'}]
// check if both fields have errors
	// if yes, return:
	// [{ field: 'email', message: 'The provided input is not a valid email'}, { field: 'password', message: 'The password needs to be longer than 8 characters'}]

Now, you might be tempted to write out your code exactly like you outlined the logic. This might leave you with a tangled mess like the following:

const { email, password } = inputs;

if (!isValidEmail(email) && !password.length < 8) {
	return [
		{
			field: 'email',
			message: 'The provided input is not a valid email'
		}
	]
} else if (isValidEmail(email) && password.length < 8) {
	return [
		{ field: 'password', message: 'The password needs to be longer than 8 characters'}
	]
} else {
	return [
		{ field: 'email', message: 'The provided input is not a valid email'}, 
		{ field: 'password', message: 'The password needs to be longer than 8 characters'}
	]
}

Even if you have not learned to code yet, you can probably see that this logic is not well written. There is a lot of duplication, and it will require a lot of work to add more errors to the form.

Let’s try to break out the logic in a better way, while accounting for all the scenarios defined in our comments. At this point it makes sense to break out our logic into separate files to keep an overview. Let’s first define all rules we have:

// validation-rules.js

const validEmailRegex = /\S+@\S+\.\S+/;

export const isValidEmail = field => email => {
	if (validEmailRegex.test(email)) {
		return {
			field,
			message: 'The provided input is not a valid email'
		};
	}
	
	return null;
}

export const isShorterThan = length => field => input => {
	if (input.length < length) {
		return {
			field,
			message: `The input needs to be longer than ${length} characters`
		}
	}

	return null;
}

With our generic rules defined, we can then define the rules we want to assign to each input field:

// validation-rules.js

const emailRules = [
	isValidEmail
].map(rule => rule('email'));

const passwordRules = [
	isShorterThan(8)
].map(rule => rule('password'));

export const validationRules = {
	email: emailRules,
	password: passwordRules
}

At this point we have everything we need to loop through our input fields and check for any validation errors.

// validate-form.js

export const validateForm = inputs => {
	let errors = [];

	Object.keys(inputs).forEach(input => {
		validationRules[input].forEach(rule => {
			const result = rule(inputs[input]);
			if (!result) {
				return;
			}
			errors.push(result);
			return;
		});
	});

	return errors;
};

This looks still a little complicated. You might even argue that we have written more code.

But our code has become flexible and extendable. How about adding another rule to one of the existing fields? No problem. We just add it to the rules array. What if we want to add another field? That’s also easy by adding another rules array. The rest of the code will still work as expected.

This is why we want to reconsider our code after we have written it in a linear fashion. This practice is called refactoring.

In the next section I will show you an even more powerful technique to see future realities and refactor your code safely.

Write tests first

Writing out in simple comments what you want to code is a good start. But you will discover problems with it soon. When you refactor your code, will it still work as you wrote it out initially? You might have changed your logic entirely while reshuffling the different parts of your code. Tests will help you safely refactor your code.

Testing is often treated as an advanced topic in programming. I encountered it late in my learning journey — and often only as a side topic. I believe now that testing should be one of the first things you learn because it can give you confidence in the code you have written.

A test is a repeatable algorithm that checks whether a piece of your code provides the correct outcome — given a pre-specified or random input. When you pre-define the possible inputs and outputs of your code you are already playing through all the possible scenarios in which your program can run. Afterwards, it is much easier to write the actual code that fulfills those scenarios.

Testing can be a pain sometimes, especially when your code is not clearly separated. But you will see that writing tests first will speed up the overall time it takes you to complete any programming task. The tests written are a mental roadmap for your development. Tests are not only valuable to you, but also as documentation for your colleagues.

Don’t be intimidated by testing and start small. Try to test a small piece of code, such as a function, in all its scenarios.

Let’s write some simple tests for our validation logic. We write tests in a specific form that is easily recognizable in the future. Let’s first right down what we want to test in plain English:

describe('Email Input Validation', () => {
	it('should add validation error when not a valid email', () => {
		})
	it('should add no errors when email is correct', () => {
	})
})

describe('Password Input Validation', () => {
	it('should add validation error when shorter than 8 characters', () => {
	
	})
	
	it('should not add errors when password is correct', () => {
		
	})
})

Now let’s add the code that tests our implementation of these functions. Remember, we provide an input and expect a certain output.

describe('Email Input Validation', () => {
	it('should add validation error when not valid email', () => {
		const inputs = {
			email: 'fake',
		}

		const errors = validateForm(inputs);
		
		expect(errors).arrayContaining({
			field: 'email',
			message: 'The provided input is not a valid email'
		});

	})
	it('should add no errors when email is correct', () => {
		const inputs = {
			email: 'correct@email.com',
		}

		const errors = validateForm(inputs);
		
		expect(errors).toHaveLength(0);
	})
})

describe('Password Input Validation', () => {
	it('should add validation error when shorter than 8 characters', () => {
		const inputs = {
			password: 'short',
		}

		const errors = validateForm(inputs);
		
		expect(errors).arrayContaining({
			field: 'password',
			message: 'The input needs to be longer than 5 characters'
		});

	})
	
	it('should no errors when password is correct', () => {
		const inputs = {
			password: 'longenough',
		}

		const errors = validateForm(inputs);
		
		expect(errors).toHaveLength(0);
	})
})

These tests can be expanded to check for combinations of the two inputs.

Can you see now how powerful tests are? You have just defined the scenarios your code can take — not only in your head, but documented. You can now write and refactor your code to your heart’s content. As long as your tests are well-defined, they will warn you when your code stops working.

Anticipating how your code will be used and run is one of the biggest abilities you can acquire as a programmer. I have shown you two ways to immediately practice this skill while you are writing code for your projects or assignments. Now onto you!


  1. Jordan Ellenberg covers the perpetual fight over how to teach mathematics at some length in his fantastic book "How Not To Be Wrong".

  2. https://marvelcinematicuniverse.fandom.com/wiki/Doctor_Strange#Powers