Exercism is a tool that lets you download and solve practice problems in over 30 different languages. I mentioned it in passing before, but let’s talk more about why it’s great.
First of all, each problem in Exercism has a set of unit tests, so you don’t have to wonder if you’re doing it right, you can just run the tests and know for sure. The tests are also great for experimenting with your code and seeing if you can make it easier to read or easier to change without breaking it.
The problems are also carefully chosen to help you learn concepts that are important to each language. Just because you can solve a problem in a certain language on a coding challenge site like HackerRank doesn’t mean you’re learning anything interesting about that language in particular. I know that’s not really what challenge sites are meant for, but I’ve seen them recommended to a lot of people who are learning and think it’s important to be clear about what challenge sites are good for (general programming concepts) and what they aren’t necessarily good for (learning individual languages).
And finally, Exercism directly incorporates both giving and receiving feedback. Obviously getting feedback is helpful – to directly quote the Exercism site: “You can write FORTRAN in any language, as the saying famously goes, but with enough feedback, you’ll quickly find yourself writing the language the way it wants to be written.” – but giving feedback is seriously underrated. To tell someone what you think of their code, you have to read it carefully and then think seriously about what makes code good or bad. That’s enormously helpful when you’re new and don’t really understand what “good code” means yet, or when you’re new to a language and just don’t know the best way to do things in that particular language.
Testing! Getting better at testing will make you seem like a better programmer even if your coding style doesn’t change at all. No matter how beautiful and clear your code is, if it’s full of bugs it’s not good code.
It’s kind of ironic that I’m writing a post about testing because honestly I’m not very good at it. Better than I used to be, especially since I started working at a company that actually has unit tests and insists they all pass before you push anything to production, but testing is still not one of my strengths. You don’t have to be amazing for it to be worth doing more testing, though. Some improvement is always better than none.
The thing most programmers, including me, seem to struggle with the most is not being able to think of anything but the happy path through our code. It’s like how when you’re trying to proofread your own writing you see what you meant, not what’s actually there in terms of typos and missing or repeated words. We test the way we meant our code to work instead of thinking of how it could break, and then we decide testing our own code is a waste of time because QA always finds more bugs anyway.
One of the best strategies I’ve found for avoiding getting stuck in the happy path is to plan out how you would test your code before you write it. You can’t get stuck only testing the way you meant your code to work if you haven’t written it yet :) You do need to have some idea how the feature as a whole is going to work, but if you don’t have that then you shouldn’t be worrying about testing anyway. Figure out what it’s supposed to do and then you can think about testing.
This works even if you’ve gotten as far as defining interfaces. Just take a minute and jot down some notes about what values could possibly get passed into those interfaces. Not what should be passed in, not what would ever be passed in by a reasonable human being who doesn’t personally hate you, but everything that the language itself would ever allow. This blog post On Testing is an extension of a joke tweet but is actually a great place to start if you’re not sure what sort of input you should be testing. And if you work in Java like me, make sure you handle nulls. Just because that parameter should never ever ever be null doesn’t mean you don’t have some messed up data somewhere in your system that will produce a nothing where there should be a something. And don’t forget to test with bad data so you can make sure errors are displayed when they should be and are spelled correctly.
If you do unit tests at your company testing thoroughly is a lot easier, but even if you don’t you can still manually test at least a few different cases. It’s just embarassing when you go to demo your new feature to someone and it immediately blows up.
Another part of testing, and for me the hardest part, is testing for my changes affecting existing code in non-obvious ways. It’s really easy to fix “surprise that parameter can be null” bugs and much harder to figure out why on earth adding a new feature would break an existing one that didn’t seem to be related. On the upside for developers like me, the entire reason regression testing exists is to catch bugs like this. Unfortunately, full regression tests aren’t feasible at every company for every release.
All I can really recommend to prevent the weird bugs is to isolate functionality as much as you can, which is good coding practice anyway. That is, if your app formats emails, all of the email formatting code should be in the same class if possible or the same package if you need more than one class. The less different features interact with each other, the less chance you have of those features getting in a fight :)
To bring this back to becoming a better developer, the fewer times QA (or god forbid, your customers) have to kick back a feature because it has bugs, the better a developer you are. Taking the time up front to make sure your code works is absolutely worth it for the time savings later and the increase in quality. Even if you deliver more slowly than programmers who do less testing, QA/your project manager/your team lead will notice whose features zip right through QA and whose get sent back over and over. And if they don’t, you should remind them repeatedly :) Going to QA first means nothing when it takes try after try after try to get approved.
One of the best things about testing thoroughly is that it’s a work thing you can do at work that doesn’t affect your personal time. It’ll even save you time in the long run!
I’m not sure what possessed them to do it, but someone wrote a micro webframework in gnu COBOL. There’s even a github repo if you’d like to run it yourself. I love how weird projects can get when programmers build something just because they can.
Back in part 1 I talked about how important it is to make sure you understand the problem you’re trying to solve. Today I want to expand on that because there’s much more to problem solving. Having a great understanding of the problem you’re trying to solve is great, but it’s not always enough. Sometimes you’re wrong about what the problem actually is. No matter how well you understand the problem you think you have, it’s not going to do you much good if you’re trying to solve the wrong problem.
Telling people to make sure they’re solving the right problem is all well and good, but an actual example always makes things a lot clearer. Conveniently enough, I saw a great example of this problem on workplace.stackexchange.com the other day. To summarize the question quickly in case it disappears someday, the questioner wants to know if there are any alternatives to doing code reviews because not everyone likes doing code reviews. To quote part of the question:
Are there any alternative processes that could replace the code review for the goal of improving the code quality? Would it be possible to have something else instead of this process? While review may be required where software bugs kill humans, could some weaker method be sufficient where the situation is far from that critical?
An edit clarified that the reason the question asker is looking for an alternative to code reviews is because people in their organization use them to play power games and prevent other team members from contributing to the project. At this point you may be developing a theory about why I think “what can we do instead of code reviews?” is the wrong question :)
This particular question did happen to contain a great clue – there’s really no substitute for reviewing your code if you want to improve it. That’s kind of like saying you want to be a better writer but you don’t want anyone to proofread your work. When your solution goes directly against your stated goal, there’s almost certainly a deeper problem. Sometimes that problem is fixable and sometimes it’s not, but there’s definitely something there you need to look into.
Given that the reason the question asker wants to find an alternative to code reviews is because team members are using them to jerk their colleagues around, I don’t think it’s too much of a leap to the conclusion that the real problem is that people are being jerks and playing power games when they’re supposed to be working as a team and that trying to avoid code reviews is just a workaround for a serious culture problem.
To be clear I don’t blame the question asker for trying to solve the wrong problem. I’m assuming they aren’t a manager and/or don’t have the authority to tell the power game players to knock that shit off and start acting like grownups, so finding some way to avoid code reviews without completely ignoring code quality is about all they can do to work around the real problem. But if you’re going to do that, and sometimes finding a workaround/bandaid solution is the only thing you actually can do, I still think it’s important to be honest with yourself that what you’re doing is putting a bandaid on the real problem. If you forget that, you’re going to get a nasty surprise later when it turns out the real problem has popped up again in a different form.
To keep harping on the code review example, just because you’ve removed one avenue for for jerks to play power games doesn’t mean everyone is going to start playing nice. If you do retrospectives or post-mortems of any sort, jerks are going to use those to throw their colleagues under the bus and/or to take credit for their work. Whatever system you use to assign work, jerks will try to abuse it to keep the interesting/fun/easy/politically valuable tasks for themselves and leave the dregs for someone else. And no matter what you try to do to control bad behaviour in your development process, you can’t prevent someone malicious from going to lunch with their dev manager buddy and complaining that that one feature sales keeps pushing for has to be postponed again because so-and-so just isn’t contributing anything (of course they’ll leave out the fact that the malicious dev won’t approve any of their pull requests), they’re such a drain on the team.
This particular problem is especially difficult to actually solve because the real solution is for management to do their jobs and enforce consequences for sabotaging team mates and otherwise refusing to act like a professional. Making anyone, especially someone who outranks you, do their job is never an easy task, so I completely sympathize with the urge to “fix” the symptom rather than the root cause. Some problems are simply above your pay grade, others may be so complicated or expensive to fix that it’s better for the business to keep working around them.
In other cases, fixing the root cause of the problem actually is doable and cheaper or more efficient than keeping a clumsy workaround. Even then, you can’t fix the root problem without knowing what it is, so keep asking why until you get down to a bedrock answer like “Because that’s how this company makes money.”
Julia Evans has a great guide to asking good questions, you should read it :) Asking good questions is such a useful skill, I wish programming education spent more time on it.
Getting really good at asking questions is also a great hack for looking like a better developer (it’ll also help you actually become better, but in the short term it’s a good hack). When you ask a bad question, like “My code isn’t working, can you help me?” people have to wonder what you’ve tried already or if you tried at all before giving up and asking someone else. If you ask the exact same question with more detail, especially about what you tried already, like “I’m trying to send an email but I’m getting an error message I don’t understand. I tried googling it but I got a bunch of different answers and I don’t know which one applies to my problem. Can you help me sort through them?” then it’s obvious that not only did you not immediately give up, but you also respect your answerer’s time enough to make it as easy as possible for them to help you. Telling them what you’ve tried already means they can skip suggesting things you already did, and asking a specific question means they don’t have to do the work of figuring out what the question actually is before they can even start thinking about how to answer it.
People love it when you make things easier for them, and when you show them you’ve put some effort into doing so, they’ll think you’re a better programmer than the person who makes getting the real question out of them like pulling teeth even if both of you are around the same skill level. That’s the hack part :) The becoming a better programmer part is that stating a question really clearly (yay rubber ducks!) and listing everything you’ve tried already may trigger that flash of insight about what you haven’t tried but should or what assumption you made that could be wrong. If you get into the habit of reflecting on what you’re doing, you’ll learn a lot faster than someome who sits around and waits for help.
A while ago I wrote about how hash maps work, but something’s been bugging me. How does the hash function do its thing? I know hash functions make variable length data into fixed length data but how do they do that? To be clear I’m interested in the kind of hash you would use for a hash map, you would definitely want a more secure hash to keep your passwords safe.
/* Returns a hash code for this string. The hash code for a String
object is computed as s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
using int arithmetic, where s[i] is the ith character of the string,
n is the length of the string, and ^ indicates exponentiation.
(The hash value of the empty string is zero.)
Returns: a hash code value for this object. */
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
Okay great, that’s totally clear, right? ;)
Yeah, I have no idea what it’s actually doing either. But I can find out!
First of all, where are the values of hash, offset, and count coming from? They must be instance variables because they weren’t passed in as parameters. I poked around in the String code a little more and it turns out hash is defaulted to 0 when it’s declared, offset is set to 0 in the constructor, and count is set to the size of the string when it’s created.
The first thing hashCode actually does is checks if hash is 0. If it’s not, then we know we already computed the hash and we can just return it and go on with our day. Makes sense, why do the same calculation over and over again when we can just do it once and store the result? I think that’s the same reason count is stored separately instead of just calling value.length() when you need it. We know the length will never change because Strings are immutable, so why not save ourselves a lookup?
The next weird thing is how the method is adding a number to a char. Chars are characters, not numbers, aren’t they? Well, yes and no. According to the docs, a char is “a single 16-bit Unicode character. It has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive).” That 0 to 65,535 part seems suspiciously like a number :) You can also test that out yourself in the Java REPL. It turns out Java will happily treat a char like an int if you ask it to.
The rest of it is pretty simple, we’re just looping through every character in the string and adding (31 * current hashcode) + current character to the existing hashcode.
Okay, but how does that map a string of any length to a hash code of fixed length? Shouldn’t a longer String always have a larger hash code? Not if your hashcode is an integer! Those just roll over into negative numbers if you add too large of a number to them. And because 2 and -821785444 are both integers they take up the same amount of memory, which means that no matter what size String you start with, the hashcode is always the same size.
Another interesting little detail of how hashmaps actually use those hashcodes is that they rehash your hashes. If everyone used random Strings for keys then they wouldn’t need to, but because keys are usually Strings with some kind of meaning, that means the hashes for those keys won’t be evenly distributed. That is, a hashcode doesn’t have an equal chance of being any number from -231 to 231-1, you’re going to get clumps of hashes around some numbers because you’re more likely to use some Strings than others.
Great, but why does that matter? Performance! The more collisions you have (different Strings that happened to work out to the same hashCode), the more elements you need to look at to find the one you wanted and the worse your performance is. To get around that, java does some bitwise operations on the hashcode to reduce the number of collisions.
Now we all have some idea what actually happens when you use a HashMap :)
This post titled How I Ruined Office Productivity With a Face-Replacing Slack Bot (Without Really Knowing What I Was Doing) has been floating around the internets lately so you may have already seen it. Even if you have, it’s an interesting example of how a programmer breaks down a new problem and experiments with the pieces before putting it all together. I especially like how that post shows the tiny experiments Jason started with and how those built on each other.
One of the most common misconceptions I’ve heard about programming is that you have to be a math whiz to be a good programmer. I’ve mentioned this before but I want to attack that particular stereotype more directly.
It is true that there are similarities between math and programming – programming uses some math terms like function and variable, it’ll be a lot easier to get things onto the screen if you understand coordinate systems, and of course to do math or programming you spend a lot of time manipulating symbols and thinking abstractly, but you know, programming is also a lot like language. I mean, they call them programming languages for a reason. To quote Hal Abelson, Jerry Sussman and Julie Sussman’s book Structure and Interpretation of Computer Programs:
First, we want to establish the idea that a computer language is not just a way of getting a computer to perform operations but rather that it is a novel formal medium for expressing ideas about methodology. Thus, programs must be written for people to read, and only incidentally for machines to execute.
Programming is fundamentally about communication. Not only are you communicating with the machine, you’re communicating with your future self, the rest of your team, and potentially other teams if you end up working for a large enough company. And that’s just when you’re writing code! You’ve also got to make sure you’re building the right thing and let other people who are waiting for that thing know how it’s going. You know what’s great practice for communication? That’s right, English classes! Sarah Mei has a great blog post about the same point I’m trying to make in this one, here’s an especially interesting quote:
The students I saw – all adults – came from a wide range of backgrounds. People with a math background did fine, of course, but people with a heavy language background often did better.
Nobody seems to have an official study on this, but I think there’s something there. Language requires just as much abstract thought as math does, with the added benefit (to your communication skills) of failing your classes if you can’t make yourself understood to anyone else. As a bit of an aside, the biggest problems I’ve run into at work have never been problems of pure programming skill (although those ones do suck a lot), they’ve been communication problems with team members, other teams, management, other departments entirely and other offices.
And to stick another nail in the coffin of the idea that programming is math, I’ve been a developer for ten years now and the most complicated math I’ve ever had to do at work is stuff like “if I have a div that’s x pixels wide and y pixels tall and I want it centered inside another div that’s a pixels wide and b pixels tall, what should my margins be?” I used to believe that programming was math, and some programming is certainly math heavy, but it’s hard to argue with the lack of math in my day to day job over ten years and four companies.
So given that programming is not math, why do so many people think it is? I’m going to quote Sarah again because she already said it really well:
When programming was just getting started, early in the last century, we used it to solve highly mathematical problems like calculating missile trajectories and decrypting secret messages. At that point, you had to be good at math to even approach programming. Tools, such as programming languages, were designed specifically to solve mathematical problems, because those were the ones we thought it was worth spending money on. Computers were for doing math.
I think another issue is the way we teach programming (credit where it’s due, I stole that idea directly from a comment on Sarah’s post). Computers are great at doing boring and tedious calculations, plus it’s easy to tell them how to do it, so why not start teaching programming by having students make the computer calculate something for them? It makes it really easy for the students to check their work too – assuming they’re able to do the math problem themselves, they can easily check if the computer got the same answer.
That assumption can be a real problem, though. If you can’t do the math problem or don’t care about the math problem, you end up effectively shut out of programming, something you could very well be great at, because of something that has next to nothing to do with the actual job of software development.
If you can do basic arithmetic and deal with the concept of variables, congrats, you can be a programmer! That is literally all the math you actually need!
Willpower is a big deal when you’re doing an inherently difficult and frustrating task like programming. It’s also something we tend to assume we either have or we don’t, but there are actually a lot of things we can do to improve our chances of making it through a willpower challenge whether that’s making yourself write boring documentation or resisting the urge to screw around on reddit all afternoon instead of doing your work.
Kelly McGonigal gave a talk called The Willpower Instinct (not so coincidentally also the title of one of her books) about that exact subject at Google. It’s a really interesting talk and I recommend watching it, but it’s also an hour long so I’m going to list the takeaways here. Full disclosure, some of these things will take up a little bit of time outside of work, but they will definitely not eat your life and will be useful for way more than just being a better programmer.
1 Sleep! Just getting enough sleep makes it much easier to make decisions that support your long term goals/resist choices that sabotage your long term goals. Meditation helps too, that was the tool used to improve sleep in the study Kelly McGonical referenced. That study was done on people in rehab for drug addictions, and even with an urge that strong to use drugs, simple sleep and meditation made the partipants much more resistant to relapse than the control group. Something really cool about that study was that the participants got those fantastic benefits from meditating for only 10-15 minutes a day.
There’s a lot of really interesting stuff in the talk about how different parts of your brain actually work together (or don’t) when you need to use your willpower to delay gratification or work toward a distant future goal, but I’m not out to transcribe the whole talk here, you’ll have to watch it for all the details :)
2 Stop beating yourself up when you make a mistake. Beating yourself up doesn’t actually improve your willpower at all. It turns out that the harder you are on yourself when you have a willpower failure, the sooner you fail again and the worse it will be next time. The worse you make yourself feel, the more you want comfort, and the more you want comfort, the more likely you are to turn to the exact thing you’re trying to stay away from, whether that’s chocolate, alcohol, cigarettes, or excessive youtubing. It actually works much better to have compassion for yourself and just try to do better next time.
3 Learn to identify with your future self. Most of us feel like our future self is a stranger, and the more you feel like that, the less likely you are to do things to protect that stranger’s health and happiness (like exercising or saving for retirement). Who here has cursed their past self for not commenting their code? We all know that we should, but we hardly ever do it. I think a big part of that is that we somehow don’t believe the frustration our future selves will feel when they have to work on another piece of badly commented code is as bad as the frustration our present selves feel.
One way you can get to know your future self is by writing a letter from them to your present self. This can either be a general letter about who future self is and what they’re up to, or a more specific letter thanking your present self for doing the work of exercising more, quitting smoking, or actually commenting that code even though it seems perfectly clear right now.
4 Imagine yourself failing. It’s totally counter intuitive, I know! The idea is to think about how things could go wrong for you so you can come up with workarounds. Say you want to exercise more, if you know that you’re likely to put things off until it’s too late at night to go for a run, you could leave yourself notes or set reminders to make sure you actually go for that run. If you keep track of what goes wrong for you and basically become a detective of your own failure, pretty soon you’ll have a comprehensive set of workarounds / ways to avoid the things that trip you up.
It’s also really helpful if failure doesn’t come as a shock to you – it’s easier to shrug it off and try again if you’re a little bit pessimistic and go “okay, I know I’m going to fail at this a few times, what am I going to do when that happens?” Sure, it seems obvious that the answer is “get back to the thing I was doing to reach my goal” but if failure is a surprise then it’s really easy to decide, “welp, I missed two workouts this week, I’m fundamentally a failure, no point in trying again so I might as well sit on the couch all weekend watching tv” even though two workouts are just not a big deal in the grand scheme of things and you could easily get back on the wagon by working out on the weekend.
5 Learn to tolerate discomfort. In her talk Kelly McGonical mentioned that the ability to hold your breath for 15 seconds is actually a very strong predictor of how well you will do in willpower challenges. There’s a technique called “surf the urge” that relies on that idea. When you “surf the urge” you give your full attention to your craving and trust that if you just sit with it for a while, it will pass. Immediately trying to repress the urge, whether it’s to have a chocolate bar or to screw around on reddit for three hours, just teaches you that the discomfort you’re trying to avoid is intolerable and you Absolutely Must Do Something About It Right Now. That’s just not true, you’re a grownup, you can ride it out. What you do by surfing the urge is break the link between feeling stress and opening that tab, which makes it much much easier not to give in.
And finally, here are some much less evidence-backed tips from me:
A Outsource your willpower if you can – browser extensions like stayfocusd (I haven’t found a really good mobile app yet, recommendations welcome!) that just don’t allow you to spend all afternoon screwing around on Facebook mean you can take all the energy you would have spent resisting the urge to open that tab and spend it on something else.
B Build habits. Once something is a good solid habit it takes way less willpower to keep doing it. Flossing, for example, is boring and a hassle but once it’s a habit it feels way more weird not to do it than to just get it over with. I recommend BJ Fogg’s Tiny Habits course if you want to make more things a habit so you can stop thinking about them.