The following are what I’ve learned after having programmed for a little while. It is by no means a complete list, and you may disagree with some of it.
You’ll Never be Great
And that’s a good thing. The moment you think you’re great is the moment you’ve given up thinking that there’s significant room for improvement. There’s always room to learn, and you’ll be better for it.
Learn, Learn, Learn
I read Hacker News and Proggit daily. By virtue of this I also invariably end up on StackOverflow each day. What I’m looking for on these sites are articles on programming in general. I want to expand my horizons, and become exposed to new paradigms and ideas about how to go about the field. I also learn by Googling up whenever I encounter terms that I’m not familiar with.
I’ve learned so much from doing this. From functional programming concepts to how big web companies maintain responsive services in the face of gargantuan demands. It would be a mistake to dismiss this exposure on the basis of appearing unrelated to what you do. If you’re developing, it’s all related. It all matters. Those ideas shape how you think about problems, and provide you with more options with which to address them.
Don’t Over-Engineer
I define over-engineering as the act of programming in solutions to problems without having first established whether it’s warranted. I have been quite guilty of this with past projects.
The problem with over-engineering is that it increases the complexity of the code, and in doing so, reduces your flexibility to deal with the issues that do materialize. The kind of problems that have ramifications that extend beyond refactoring a few methods. Increasing the size of the code base also elevates the statistical likelihood of bugs, and if it’s not obvious, makes finding the bug that much more time consuming.
Kill Dead Code
If I don’t use it, I don’t comment it out. I delete it. If I want to go back to it in the future, I look through the history as managed by the code revision software.
Having a bunch of commented out code obfuscates the remaining logic. Leaving dead code in there, meanwhile, increases the likelihood of errors in the long run.
Only Ever Define Once
If you define something more than once, whether it’s a constant or program logic, you introduce the possibility that the program may do two different things for what is supposed to be a single behaviour. This introduces the possibility of error. Not to mention it increases the size of your code.
I have yet to encounter a situation where something that was defined twice or more couldn’t have been condensed into just the one. Also see my point on clarity over efficiency.
Don’t Program Blindly
By this I mean resist the temptation to start tackling the problem by jumping in and coding straight-away. It’s okay for small projects, where the whole thing is going to be done in a matter of minutes or hours.
When you’re dealing with larger projects, you’re going to want to work out how the program is to behave ahead of time. In my case, I’ll find flaws with the initial approach I had in mind, refine it, and then code it. The amount of time I save by removing these logical flaws at the conceptualization rather than execution stage is immeasurable.
Comment Everything
It’s not for the sake of others. It’s for yourself. Unless you’re actively working on a project, or have Rain Man-like memory, you’ll never really remember how your application works. When someone a year or two down the line asks for a new feature, you’ll have to go through the code base, and if you didn’t comment it properly, you’re likely in for a number of frustrating hours.
As important as commenting your code is maintaining those comments. You can’t just change your code and leave your comments be – that defeats the purpose of their existence. My rules are to keep it brief, and to explain the why more than the how. The code can handle the how.
Clarity over Efficiency or Cleverness
I will always gladly sacrifice a few potential cycles of CPU time if it means my code is more clear. In all my years of programming, never once has that trade-off ever resulted in my having to refactor code.
Debuggers & Profilers: Learn them
When you start out, it’s tempting to use print statements everywhere to figure out what’s wrong. Learn a debugger. It’ll save you so much time when it comes to finding the source of an issue, especially when it’s not where you expected it to be.
Profilers meanwhile help you figure out what part of your code is taking the most time to execute. It may not be what you anticipated. Instead of being that loop that does this big computation, it might actually be this side function that does a little record-keeping.
Use Useful Variable and Function Names
I’ve inherited code bases where the authors used such descriptive variable names as “AAA” and “AAAA”. Nearly as useless are when variables with redundant names like “data” are used.
Variable and function names aren’t for the compiler or interpreter’s benefit. They’re for the person who wrote the code. Like comments, putting a little effort in coming up with descriptive names will save you much head scratching when you’re working on that same code down the line.
Indent Properly
Indentation is another of those things that in most languages isn’t for the benefit of the program interpreting your code, so much as the authors of that code. And even then.
Proper indentation makes your code more legible, which in turn saves you time. And bugs. I didn’t think this needed to be said until I inherited this spaghetti code of an web-based management system, where each page of VBScript was a minimum of 3000 lines, none of which were indented in any logical manner.
Limit the Number of Characters per Line
In all of my IDEs, I make use of the option to draw a line that delineates the 80 character mark. I make sure that my lines of code never pass this threshold.
You should never have to scroll horizontally to see a line of code. A line represents a statement, a single idea. Always scrolling left and right to capture them will hinder your progress at absorbing the logic behind the code.
Blame Yourself
When something goes wrong, and it doesn’t seem to make sense, it becomes tempting to blame whatever you’re using. My code is behaving as it’s supposed to – it’s this graphical toolkit that has a bug in it.
No, it’s your code. Especially when you’re writing basic stuff using mature libraries and compiler/interpreter. It’s not impossible that there’s a bug in what you’re using; it’s just so much more likely that the issue you’re facing is of your own doing.
Don’t Redesign the Wheel
You’ll often encounter situations whereby you can either write your own implementation of something, or use one provided by a library. Unless you want to do this as a learning exercise, always pick the library.
There’s a few reasons for this. For one, it reduces the amount of code you have to write. This saves you time, and reduces the potential for error. It also takes into account the fact that the people who wrote the library spent their time writing that specific functionality – and they likely did a much better job than you could. Especially when you go into such tasks as signal processing or visualization.
Use Code Revision
Even if I’m working alone, I always use code revisioning software. For one, I find the historical aspect very helpful. To go back in time, and see how I did things differently. Or to figure out what changes I did when the symptoms of a bug started presenting themselves.
I name my software builds according to the revision of that build. This means that when a user says they saw an issue with build 266, I can go and load up the code for that very build, and have the same software they run. All at the press of a few buttons. The code revision software is also the authoritative changelog.
Don’t Get Attached
When I code, I code with myself in mind. How would I want this to be? Then I do it. But the way I envision things and the way users envision things don’t always align with each other.
In those times, it’s important to be able to let go of what you thought was the best solution, and cater to the user. Because this program ultimately isn’t for you, it’s for them.
Keep Functions/Methods Small
Most of my functions are only ever about thirty lines of code. That doesn’t inhibit me from creating complex applications at all. What it does do is keep the logic legible and clear, which is a great time-saver when revisiting code.
It also keeps my code more flexible. It’s much easier to alter how things work when you only have to edit a few small functions rather than mega-functions here and there.
Keep source Files Small Too
Smaller files take less time to parse mentally. Give these files useful names, and store them in directories with equally informative names. This will cut down the time you spend hunting for specific logic.
Beware the Waterfall
The waterfall approach to software development is the most intuitive on the outset, but it has a great number of short comings. This is why some teams use alternative models such as Agile. For the uninitiated, the waterfall model consists of coming up with requirements for the software, then creating the product, then testing it, and then maintaining it all.
The problem is that verification stage. You may have spent a really long time in development, and then in testing, the user realizes that how you went about something isn’t what they had in mind. Changing that one thing, meanwhile, becomes a big undertaking because it means changing a whole slew of other things along the way. You did the application with a given logic in mind, wrote it with that logic in mind, and this user’s requests pulls the rug from under all of that.
It’s easy to blame this on getting the requirements stage wrong, but you never really had any chance to begin with. The problem meanwhile with doing things differently, such as getting users more involved to get feedback early on, is that managers may not want to allocate time. They might not see the value of spending more time up front to save it down the road. I have no advice there, just be aware of the issues.
Don’t Adopt too Early
It can be tempting to adopt cool new technologies as they come out. While I love toying with new programming frameworks, I won’t use them in production applications I intend to support down the line.
The reason being that I prefer stability over features. The extra time I spend up front will be recuperated in maintenance done down the line. This does go with my environment, where I create it and leave it so that I can work on the next project. If I was in a situation where the software was a living breathing entity, I’d likely be more open to using less proven technologies.
Ignore the Fanboys
I have yet to encounter a programming language, coding style, or paradigm that was perfect. Yet there appears to be no shortage of people who snub others based on this “one article” they read.
I believe that especially as it comes to languages, it’s about picking the right tool for the right job. C is great for low-level work, but not my first pick for setting up elaborate GUIs, and so forth.
Most fanboys tend to be good at repeating, but short on understanding. When you hear one blast you for your decision, ask them why such-and-such is better. If they can provide a suitable answer, awesome – listen to what they have to say. But if it’s ignorant tripe like “Python is slow at multithreading because it doesn’t do out-of-order execution”, run. Fast.
Never Say “This Will be Easy”
Never say “this will be easy” unless you’ve already done it. Oh sure, it should be easy. But then you do it, and you run into obstacles you didn’t even know were there. Like your company’s network policy that blocks your HTTP requests. Or permission issues on the user’s computer.
Be especially weary of people express that belief of ease for you. That’s a red flag that they don’t know what they’re talking about.
Admit When You Don’t Know
University teaches you to come up with bullshit if you don’t know the answer to something. That doesn’t cut it in a professional workplace. When you don’t know the subject under discussion, don’t fool yourself. Admit that you don’t know.
Especially in a team setting, it’s an opportunity for others to play to your strengths, and help you on the rest.
Take Ownership for Mistakes
This is hard. As soon as you recognize something wonky, tell someone. The worst you can do is let it be and hope it gets better. Luck is the least reliable ally in getting you out of a bind.
A healthy work environment will take that information, work towards resolution, work out how to prevent it in the future, and move on.
Don’t Work Weekends
If you need to work through weekends in addition to weekdays, then start applying for another job. Your health and mental well-being are being sacrificed for a quarterly report.
If you’re volunteering that time, then you’re making a mistake. It can be very easy to open up the laptop and just work. I’ve done it. But there’s value in that time apart. If you spend all your time on a given problem, then your mind will narrow onto a particular solution. You will inhibit yourself from conceptualizing anything else.
Meanwhile, if you just stop focusing on it, you might spuriously come by a novel and entirely superior alternative approach. The mind works in funny ways, one being that creativity works best when there’s plenty of time not spent trying to innovate.
Treat the Cause, not the Symptoms
Bugs have causes and symptoms. A tendency with new programmers is to blindly change things around until the symptoms disappear, without ever grasping the underlying issue.
When you’re confronted with a bug whose causes aren’t immediately obvious, stop for a moment. Think about the problem. Understand why it is happening. Then think out how you’re going to address it.
Leave Optimizations to the Compiler
Don’t try to optimize for the sake of optimizing. Leave that to the compiler. For one, what you’re changing might not actually be the part of your code that’s taking up the most resources. For another, most optimizations obfuscate the program logic to developers, introducing new avenues for errors. They also often reduce code portability.
Only optimize when there’s a demonstrable need to do so.