If you share my obsession with software design (and maybe even if you don’t) you’ll enjoy this talk by Katrina Owen called Overkill. The basic idea of the talk is the use of a simple toy problem as an excuse for focused practice. I really like the idea of going back to basics as a path to mastery, that’s actually why I’ve been on my own back to basics kick on this blog.
If you like that talk, Katrina Owen co-authored an object oriented design book with Sandi Metz (who is also fantastic) called 99 Bottles of OOP. I haven’t read all of it myself yet, but I have watched (well, listened to) a few of Katrina’s and Sandi’s talks and really enjoyed them.
You know that feeling when you need to get something done and you just don’t want to? Considering this post was published on a Monday, I bet you do :)
When I don’t feel like doing something, I find it can really help to trick myself. That is, I do a tiny little piece of something that’s related to the thing I need to do but that I can tell myself isn’t the real work. For example, if I’m starting a new feature, I might tell myself I’m not really starting it yet, I’m just poking around in the existing code a little and seeing where I could put it if I actually was starting that feature. Or I might tell myself that I’m not really building that feature, I’m just stubbing out a class and writing a few comments about how I would build if it I were doing that, but I’m totally not, I’m just writing comments, nothing to see here ;)
Minus a certain amount of flailing around trying to figure out the best way to do something, this is basically my process when I’m building stuff:
Don’t do the thing, just poke around in the code a little.
Don’t do the thing, just make notes about what you find.
Don’t do the thing, just write down ways you could do the thing if you were going to (but you’re not).
Don’t do the thing, just stub out a class (if needed) where you would do the thing.
Don’t do the thing, just write some comments where you would do the thing.
Don’t do the thing, just stub out a method.
Don’t do the thing, just pseudocode one method.
Don’t do the thing, just fill in one method.
Don’t do the thing, just write down one thing you’ll need to test.
Don’t do the thing, just pseudocode one test.
Don’t do the thing, just fill in one test.
Honestly, that’s the core of software development. Software design and architecture are certainly important too, but I’m convinced the single most important part of actually building stuff is breaking down problems into manageable little pieces. If we had to think about the entire application all the time, we’d all end up under our desks weeping quietly. Only the ability to ignore the big picture for a while and focus on one tiny piece allows us to actually get anything done.
Of course just because “break the problem down into tiny pieces” sounds simple doesn’t mean it’s easy. It’s really common to get side-tracked trying to figure out how something unrelated works (I have the worst time leaving stuff alone even it’s probably not relevant to what I’m currently doing), or to spend far too much time trying to figure out what the best way to do something is (that one’s so common we have a word for it), and sometimes you just don’t know where to start or the problem has so many moving parts that you feel overwhelmed and quietly freak out a little.
On the upside, learning to break problems into manageable pieces gets easier with practice. For me it’s mostly a matter of reminding myself that I’ve done this plenty of times before and I’ll solve this problem too.
If you’re having trouble getting started, see if you can trick yourself into doing something. Remember you don’t have to solve the whole problem at once, you can start with a tiny piece of it.
I really enjoyed this post about how Queues Don’t Fix Overload, it does a great job of explaining why bolting something onto your application without digging into the root cause of your problems will just make things worse in the long run.
Bonus link: I found that post in the Programming Beyond Practices newsletter by Gregory Brown, which I also recommend. Have a look at the archives, there’s some good stuff in there.
Not long ago I read this blog post Why Learning to Code is So Damn Hard, which makes a really interesting point about the “desert of despair” between beginning to learn to code and having the skills to build a complete project on your own. The desert of despair, according to the post (which I agree with) is a combination of lack of resources for learners who are between beginner and expert, and having learned enough to know how much you still have to learn. Many aspiring coders get lost in the desert of despair and never find their way out.
I also read Amy Hoy’s post 5 Things I Wish Somebody Told Me Before I Founded My SaaS. One of the points I found the most interesting was that there’s always another inflection point. By inflection point, she means “those knife-edges you tip toe along, when things could go either way, when you are, say… trying to grow your team but lack the time/revenue to do it, but growing your team would help increase the time/revenue to the point where you could grow your team. Ouch. To me, that’s the prototypical inflection point.”
Where this ties back to the other post is where Amy says “Over the last 8 years running this biz, I have said so many times: “We just have to get through this $INFLECTIONPOINT and then things will get easier.”” Doesn’t that remind you of programming?
If I can just learn enough to get my code to compile then things will be easier.
If I can just learn enough to add a feature to an existing project then things will be easier.
If I can just learn enough to start a project on my own, then things will be easier.
If I can just learn enough to break my code down into good methods, then things will be easier.
If I can just learn enough to break my code down into good classes, then things will be easier.
If I can just learn enough to break my code down into good layers, then things will be easier.
If I can just learn enough design patterns, then things will be easier.
If I can just learn this new framework, then things will be easier.
If I can just learn this new style of programming, then things will be easier.
If I can just build enough different projects, then things will be easier.
If I can just learn enough…
There’s always going to be something else to learn, it’s never going to become easy and stay that way. Things do get easier once you find your way out of the desert of despair, but initially learning to code just leads to worrying about whether your code is good leads to worrying about design and architecture and scalability and reliability and security and those are seriously hard problems.
Despite how it may sound, I’m actually not trying to say that coding will suck forever and you should just give up. I’m saying that it’s normal to feel like you still suck at this even after years of working as a programmer. You don’t actually suck, it’s just normal to feel that way. What’s really happening is you’ve levelled up. When you do that you see the problems that used to give you trouble as too simple to count and focus on the stuff you aren’t good at yet, which can easily make you think you suck.
The thing I struggle with is design. I’ve been at this for ten years now and I’m still never sure if I’ll regret the design I’ve come up with the next time we add a feature to the system. Plenty of times I’ve come up with something that seemed like a good idea at the time, only to find out that it had serious flaws when I came back and changed it later.
It’s tough sometimes but on the upside, you will never ever run out of things to learn as a programmer :)
Much like development is maintenance and a creative field, development is also communication. One of the hardest problems I’ve run into with programming isn’t the programming itself, it’s making sure I’m actually building the right thing and fully understand the requirements.
Just because it’s obvious to you that a feature should work a certain way doesn’t mean it isn’t equally obvious to someone else that it should work a different way. Not that I’ve ever gotten burned by that or anything ;) As overly simple as it sounds, it can be really helpful to talk your plan for building a feature over with the person who came up with the requirements and your team lead. Something as simple as having a quick chat about how you’re going to build a feature can uncover assumptions you’ve made that aren’t right or things you missed or even existing code you didn’t know about that could really help.
If you’re working directly with other developers on the same project, it’s possibly even more important to communicate with them. At the very least you need to figure out who does which piece and how your code will work together. If you can divide a project into nicely separated pieces with an interface or API between them that definitely makes things easier, but you still need to keep in touch. Interfaces can change as you build and discover what you actually need, parts of the project can be delayed, there are all kinds of things other people on your project might want to know about.
Even if you’re not on the same project, it’s still useful to communicate with your team. If nothing else, your team lead needs to know how your task is going so they can get ready to assign you more work or re-assign a task they were going to give you if you hit a snag.
Stubbornness is more important than intelligence or talent when you’re learning to code, but that doesn’t mean it’s useful all the time. Listening to other people’s ideas is just as important as refusing to give up. It’s tempting to stubbornly insist on doing things your way, but that’s only going to hurt your project and piss off your team. An idea being yours doesn’t mean it’s the best way to do it. Of course, another dev, even a more senior one, having an idea doesn’t magically make that one perfect either, that’s why it’s so important to talk things out. It’s nice when you get to implement your own ideas, but it’s best for your project if you implement the best ideas you have available, whether those came from you or from someone else on your team.
Whether or not you actually take their suggestions, it’s really important to listen to your team. Despite what you may have heard, software development involves surprisingly little coding in a basement far away from all human contact. Professional software development is fundamentally a team sport, very little of it is so small that a single person can build and maintain an application by themselves. Working with a team means you need to get along with them and listening to them is a big part of that. If you bother asking for anyone else’s opinion, you need to give it a fair shot and seriously consider implementing it. If your mind is already made up, do the polite thing and don’t waste people’s time by asking for opinions you’re not actually going to listen to.
The preceding rant is brought to you by the many horror stories I’ve read online about people who label their listening problems someone else’s communication problem. Listening is communication. Without it, no information gets shared.
Communication skills are at least as important as hard development skills if you want to make a living as a developer. If I had to choose just one, I’d say communication is more important. Someone who will listen to me and explain what they’re doing can learn to code or code better. There’s nothing you can do with someone who can’t communicate. That doesn’t mean you have to be perfect at it – I’m certainly not – just that you’ve got to try.
One of the many things that it took me a while to get a really solid handle on after college is what good code actually is. As a beginner, my idea of good versus bad code was solely based on whether it worked or not, it didn’t make sense to me that working code could be ‘bad.’ I was reading this blog post about writing good code (it’s really good, you should read it) the other day and thought I should talk about what good code actually is and why it matters.
First of all, a piece of code that works is always better than one that doesn’t. Credit where it’s due, I believe it was Deryk Barker who told us that back in college. Having an elegant, clear design for your application is great and all, but an application that actually does things is better than a perfectly designed application that doesn’t exist yet. Your design is never going to be perfect anyway, and if you want to accomplish anything eventually you need to write some code.
But once you have a working application, whether your code is bad or good matters a whole lot. Development is maintenance. That is, if anyone ever uses your code, it will eventually need to change, and if your code is bad making those changes will be much harder than it has to be.
Understanding what makes code difficult to change is something that just comes with experience, none of my projects in college were long term enough for me to really understand what makes code hard to change. In other words, you shouldn’t feel bad if you don’t understand what makes code good or bad right away, it’s a tough concept. It’s also a matter of taste. There are certainly some things that are near universally loathed, but there are many more things that are matters of personal preference.
Let’s start simple and work up to the more complicated stuff.
One of the simplest parts of good code is just formatting it consistently. One of the reasons I think python is a great first language is because it forces you to indent your code properly. It sounds pointlessly picky, but indenting is really, really important. Especially in HTML, I can’t tell you how many times I’ve failed to close a div because I didn’t indent my code nicely and didn’t see the problem. Consistent formatting not only helps you spot errors, it makes your code a lot easier to read. You know how your English teacher hounded you about proof-reading your essays until your spelling and grammar were perfect? That’s because it’s jarring to be happily reading along and suddenly hit something that looks wrong. Code is very similar, the more different each piece is the more time you spend trying to figure out what’s going on. Every fiddly little detail takes up more space in your brain, the more of them you can take for granted the more room you have left for what the code is actually doing.
Another formatting issue is taking advantage of language quirks like not always requiring curly braces for if statements. Java, the language I’ve used most, allows you to leave out the braces if the body of your if is only one line. I hate that a whole lot because it’s a trap: if you add another line to that if but you forget to add braces, that other line won’t actually be part of the if block and your code won’t do what you meant. You should always code to make it as difficult as possible to screw up, and leaving out braces makes it easy to screw up later.
Another big deal is having good variable and method/function names. I’ve talked about this before, but it’s worth repeating – variable names matter! Sure, you know what you mean right now, but will you remember why you named that variable that way in six months? You will not, so give your variables nice clear names. If that means your variable name has to be a bit longer, that’s fine. You’re going to read that code many more times than you write it, it’s really not worth worrying about making variable names easy to type.
Besides being descriptive, names need to be consistent too. If you name three methods handleNewOrder, handleCompletedOrder, and handleShippedOrder, your cancel method needs to be named handleCancelOrder. If you have an extremely small application it doesn’t matter so much, but in my experience most professional programming is done on applications much too large for anyone to just remember all the method names. If your method names are consistent, you can pretty much guess what they’re called and use another piece of code without having to dig through it to figure out what on earth the order cancelling method you’re sure exists somewhere is actually called. That’s a big waste of time
Methods/functions also need to do one thing and only one thing. This is one of those things that will make much more sense once you’ve worked on a larger project. Methods that do too much are a pain to update because you have to sort out which part of it does which thing and where you can safely make changes to one part without breaking the other one. They’re also a hassle if new code only needs part of the functionality of the megamethod, now you’ve got to either refactor it into more methods that only do one thing each or duplicate part of the functionality. Both of those are risky, especially if you don’t have automated tests.
Speaking of tests, opinions vary but I think they’re a really important part of good code. It’s really helpful to be able to make a change, run the test suite, and be reasonably sure (no test suite is perfect) that you didn’t break anything that used to work. Sometimes parts of your application will interact in ways you just didn’t think of, comprehensive tests can catch that before you migrate something to production that will ruin your support team’s day.
At a level up from methods, your objects should just be in charge of one thing and have reasonable names too. For example, if you have a simple todo list application, you probably want a user object and a list object, and you really don’t want the user object to mess with your list or your list object to update your user. The easier it is to guess where a particular function actually happens in your code (updating a user, for example), the less time you waste digging around trying to figure out where on earth users get updated.
Application architecture is also an important part of good code, but this post is already quite long enough :) The TL;DR version of architecture is that your objects should be grouped in predictable ways just like your methods are grouped into objects in predictable ways. More on that later!
The reason I like this flowchart so much is that it’s really clearly broken down into steps and doesn’t drown you in information you don’t need. A lot of git guides start talking about branching and merging right away, and while those are important concepts they’re not necessary if you just want to put a simple project up on github so other people can see your code.
If you write javascript, you probably use console.log() all the time. The other day I stumbled across this video (quite possible in javascript weekly, a newsletter I recommend if you work in javascript regularly) with a really great and very simple tip: if you’re logging an object that contains other objects, use console.table() instead. I didn’t know that was a thing! It gives you really nice easy to read output in the form of, you guessed it, a table :)
Here’s the video so you can see what I’m talking about:
Binary search is an extremely simple idea that’s useful for much more than finding an element in a sorted array. The way binary search works is you compare the item you want to find with the item in the middle of the array, then whichever “side” of the midpoint of the array your element falls on, you compare it to the midpoint of that half until you find the item you want.
According to wikipedia a binary search makes at worst O(log2n) comparisons, which is pretty great when you have a large array. Because the search halves the search space each iteration, the maximum number of iterations you’ll need is the number of times you can divide your array in half. For an array of 100 items, you should only need 7 searches, and for an array of 200 items, you only need 8, and for 1000 items, only 10 searches. See how slowly the number of searches grows as the array gets a lot larger? That’s just cool!
Also cool: you can use that concept for more than just finding stuff in arrays. Back in college they taught us to narrow down where a bug in your code is using a binary search. Basically, comment out half of your code and see if the bug still happens. If it doesn’t, uncomment half of the commented half. If it does, comment out another half of the uncommented half. It always felt weird to do that, like I should’ve just been able to see the problem by reading the code, but it worked. If you’re new or just totally stumped, give it a try.
Of course, binary search isn’t the only way to find things. Hash maps can be even faster, but all they can tell you is whether your target item exists or not. If you want to return the next largest or smallest item in the event that you don’t find an exact match, hash maps are no help at all. There are binary search trees too (which I’ll go into more detail on in a future post), but ironically even though they’re named binary search trees, binary searching an array is usually faster. The problem with binary trees is it’s hard to keep them perfectly balanced, so you might have more “halves” on one side than the other which messes with your search efficiency. What binary trees are good for is quick updates – it can be a real hassle to add or remove an item from an array, trees are much easier to work with. There’s also plain old linear search, where you start at one end of your array and look at every item until you find the one you want. If you’re not going to search your array enough times for it to be worth sorting it, linear search is good enough.
That kind of tradeoff can actually make algorithm questions interesting. I still don’t care even a little bit whether you looked up how to reverse a binary tree before the interview, but I care a lot if you think to ask if you’re going to search that array enough times to recoup the cost of sorting it. Programmers, myself included, can be terrible about overengineering to solve problems that don’t actually exist. But more on that in another post!
So last Monday I talked about how frustrating programming can be and how important sheer bloody-mindedness is if you want to be a programmer. If that made you wonder why on earth someone would choose to do this, I couldn’t blame you :)
I love programming because I love building things. It’s just fun to make stuff – who doesn’t love LEGO? But even better than LEGO, if you mess up when you’re programming you can just revert to an earlier version, and if you want to experiment with an idea that might not work out you can create a separate branch for it.
While programming can be frustrating, there’s no physical material I know of that’s anywhere near as easy to work with. Buildings, machines, and even sculptures need to obey the laws of physics where the biggest restriction on programmers is our own ability to design a system. There are certainly things that are likely to get you into trouble, but with code as with writing, you can break all sorts of rules if you’re good enough to get away with it.
Programming is also a lot like solving a puzzle, something else I’ve loved since I was a kid. One of my favourites was the IQ block – I had a plastic one that came in a little case and it was so satisfying to get all the pieces to fit back inside it. I get the exact same satisfaction out of fixing a bug or getting a new feature working, but even more so because programs have so many moving parts. If you enjoy puzzles, code is about as complicated of a puzzle as you can find.
I get bored pretty quickly if I feel like I’m doing the same thing over and over, so the constant learning when you’re programming is great for me. There’s always something new to learn whether it’s another language, a design technique, a new tool, a new technology, or just a new way to do something. If you go into programming, you may get frustrated but you’ll never get bored :)
While professional programming is a generally a team sport, it’s amazing how much one programmer can get done on their own. It takes a factory to build a car and a team to build a house, but one programmer can knock out app after app if they want to. It’s just cool to look at something that’s actually useful to other people and be able to say “I built that.”
And yes, if you’ve read The Mythical Man-Month, my post was indeed inspired by Fred Brooks’ explanation of the joys of the craft.